当前位置: 首页 > news >正文

Java并发编程(2)


ThreadLocal

1、ThreadLocal是什么

  ThreadLocal就是线程本地变量,若创建了一个ThreadLocal变量,那访问这个变量的每个线程都会有这个变量的本地拷贝,但多个线程操作这个变量时,实际是操作自己本地内存里的变量,可以起到线程隔离的作用,避免了线程安全问题。

 

//创建一个ThreadLocal变量localVariable
/ /创建⼀个ThreadLocal变 量
public static ThreadLocal < String > localVariable = new ThreadLocal < > ();//写入:线程可以在任何地方使用localVariable
localVariable.set("xxxx");//读取:线程在任何地方读取的都是它写入的变量
localVariable.get();        //xxxx

2、你在工作中用到过ThreadLocal吗

   用到过,比如在登陆的时候,用户每次访问接口在请求头都会携带一个token,在控制层可以根据这个token,解析出用户的基本信息。由于在后面的服务层、持久层都会用到责怪用户信息,这时候就可以用到ThreadLocal,在控制层拦截请求把用户信息存入ThreadLocal,这样在其他任何地方都可以取出T和read Local中存的用户数据。

  很多其他场景如cookie、session、数据库连接池都可以用ThreadLocal。

3、ThreadLocal怎么实现的

   每个Thread对象里,有一个成员变量ThreadLocal.ThreadLocalMap threadLocals = null; 说明每个线程都有一个属于自己的ThreadLocalMap。当调用threadLocal.set(value) 时,会发生:

  • 先获取当前线程 Thread t = Thread.currentThread();
  • 再拿到该线程ThreadLocalMap
  • 把数据存进去,形式是 <key, value>

  那这里的key和value是什么?value就是set进去的对象。key不是ThreadLocal本身,而是ThreadLocal 的一个 弱引用

  那为什么是弱引用呢?假如key是强引用,若某个ThreadLocal 对象没有外部引用了(ThreadLocal = null),但ThreadLocalMap还持有它,那它就永远不会被GC,造成内存泄露。用了弱引用之后,一旦外部不再持有ThreadLocal,GC就会把它回收。ThreadLocalMap中的key会变成null,只剩下value。JVM之后会清理这些key为null的Entry,避免泄露。

4、ThreadLocal内存泄露是怎么回事

  •  key是弱引用:
    • 外部不再引用ThreadLocal 对象,GC 会回收它。ThreadLocalMap里的entry变成<null, value>,value还在,但程序员无法通过ThreadLocal拿到这份数据。若线程是线程池里的长生命周期线程,这块value会一直留在内存,直到线程结束才可能释放-->内存泄露
  • key是强引用:
    • 即使外部不再引用 ThreadLocal,它也不会被 GC,因为 map 还持有强引用。
    • 弱引用可以减轻泄露风险。
  • 如何避免内存泄露(最佳实践)
try {local.set(new User("Alice"));// 业务逻辑
} finally {local.remove(); // ✅ 主动清理,避免泄漏
}

5、ThreadLocalMap的结构了解吗

   ThreadLocalMap是一个定制化的Map,存放在Thread对象里,每个Thread维护一个自己的ThreadLocalMap,里面的key就是弱引用ThreadLocal。它没有实现Map接口(是内部类,只服务于ThreadLocal),主要是一个Entry[] table数组(每个 Entry 保存 <ThreadLocal弱引用, value>)。

  每次创建新的ThreadLocal对象,都会分配一个threadLocalHashCode值。这个值不是简单的1,2,3...自增,而是每次递增一个特殊的常数0x61c88647。这个数来自黄金分割数(√5 - 1) / 2 ≈ 0.618...。这样可以让哈希值分布更均匀,避免冲突集中。

6、ThreadLocalMap怎么结局hash冲突的

   ThreadLocalMap使用开放定址法,这个坑被人占了就去接着找空着的坑。若插入一个value,通过hash计算后应该落入某个槽位,但这个坑已经被占了,且Entry数据的key和当前不相等,此时会线性向后查找,一直找到为null的槽位才会停止。

  get的时候,也会根据ThreadLocal对象的hash值定位到table中的位置,然后判断该槽位Entry对象中的key是否和get的key一致,若不一致,就判断下一个位置。

7、ThreadLocal扩容机制了解吗

   在ThreadLocalMap.set() 里,若存入元素时发现表里的Entry数量达到阈值(len*2/3),就会触发rehash()。

  • 清理掉已经失效(key=null)的Entry
  • 如果清理后size依然>=3/4 * threshold,就触发resize()扩容。
private void resize() {Entry[] oldTab = table;int oldLen = oldTab.length;int newLen = oldLen * 2;   // 新数组长度翻倍Entry[] newTab = new Entry[newLen];for (int j = 0; j < oldLen; ++j) {Entry e = oldTab[j];if (e != null) {ThreadLocal<?> k = e.get();if (k == null) {e.value = null;  // key 已被回收,帮助 GC} else {// 重新计算哈希位置int h = k.threadLocalHashCode & (newLen - 1);while (newTab[h] != null) {  // 开放地址法,找下一个空位h = nextIndex(h, newLen);}newTab[h] = e; // 放到新数组}}}table = newTab; // 指向新数组
}
  • 新数组翻倍:N->2N,降低负载因子
  • 遍历老数组:把旧数组里的Entrty一个个搬到新数组。若key已经被GC,就清理掉value
  • 重新计算位置:用新数组长度newLen重新取模
  • 冲突处理:若目标格子被占,就调用nextIndex()往后找下一个空位(开放地址法)
  • 更新引用:搬运完毕后,把table指向newTab。

8、父子线程怎么共享数据

  •  普通ThreadLocal不能传递给子线程,因为ThreadLocal的值存放在当前对象的ThreadLocals变量里,就算是父线程,也不算是同一个线程。
  • 解决办法:在Thread类里除了threadLocals之外,还有一个:ThreadLocal.ThreadLocalMap inheritableThreadLocals = null; 关键点在于子线程初始化时,从父线程的InheritableThreadLocalMap拷贝了一份数据。
public class InheritableThreadLocalTest {public static void main(String[] args) {ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();// 父线程设置值threadLocal.set("父线程的值");// 子线程new Thread(() -> {System.out.println("子线程获取:" + threadLocal.get());}).start();}
}

//子线程获取:父线程的值
  • 限制:只是在创建子线程那一刻复制,后续修改不同步。
  • 线程池问题:线程池里的线程是复用的,子线程不会每次都重新init(),所以默认的InheritableThreadLocal在线程池场景可能会出问题。为解决这个,阿里开源了TransmittableThreadLocal (TTL),专门用于线程池下传递上下文。

 

参考

[1] 沉默王二公众号

http://www.sczhlp.com/news/100672/

相关文章:

  • 手机网站分页设计wordpress底部不显示
  • 兰州网站建设报价深圳公司贷款
  • 浙江国有建设用地出让网站广州公司注册代理公司注册服务
  • 法华寺网站建设山东网站建设流程
  • 完整教程:WebApp 的价值与实现:从浏览器架构到用户体验优化
  • Ubuntu 安装百度网盘
  • 八字喜用神起名大师 API 接口
  • 在CentOS 7上集成cJSON库的方法
  • 公司专业设计网站做网站流行的
  • 中融木业网站是那个网站做的做网站的需要什么软件
  • 定制开发电商网站建设多少钱自己电脑如何做网站服务器
  • 公司如何做网站一般多少钱网站开发竞争性谈判
  • 做网站排名seo张掖做网站
  • 安防公司手机网站微信开发者工具代码
  • 杭州网络网站建设微电影网站源码
  • 无限成都成都市广播电视台官方网站大一网站开发项目答辩
  • 中小企业网站制作过程中要注意什么装饰公司东莞网站建设
  • 网站策划的重要性wordpress 表 权限管理
  • 作业1
  • 广州建网站加备案旅游网站建设课程设计报告
  • 社交网站建设流程邯郸学校网站建设报价
  • 张掖建设局网站广州网站制作服务
  • 知名网站欣赏window服务器如何做网站访问
  • 北京专业响应式网站建设酒店网站建设策划
  • 深圳app客户端做网站成都自然排名优化
  • 装修网站免费设计青浦练塘网站建设
  • 动漫网站怎么建设长春做电商网站的公司
  • 网站开发建设企业php空间租用
  • 济南做html5网站网站域名绑定ip
  • 广州建设网站平台中华门窗网怎么做网站