Java ThreadLocal解析
简介
ThreadLocal 类似局部变量,解决了单个线程维护自己线程内的变量值(存、取、删),让线程之间的数据进行隔离。(InheritableThreadLocal 特例)
这里涉及三个类,Thread、ThreadLocal、ThreadLocalMap
源码解析
- Thread 中有一个 ThreadLocal.ThreadLocalMap 类型的变量 threadLocals。因为ThreadLocalMap变量是跟线程绑定的,所以不存在多线程共享变量之间的并发问题,所以ThreadLocal也就是线程安全的变量。
- ThreadLocalMap 是 ThreadLocal 的一个内部静态类,没有继承java.util.Map,定义了一个Entry[]变量,通过Entry的get()方法作为key,value属性作为值来实现一个类似Map的操作
- Entry 是 ThreadLocalMap 的一个内部静态类,继承WeakReference<ThreadLocal<?>>,并且定义了一个变量value(Object类型)
- ThreadLocal 内部封装了getMap()、Set()、Get()、Remove()4个核心方法,用于操作ThreadLocalMap
- 通过getMap()获取每个子线程Thread持有自己的ThreadLocalMap实例,因此它们是不存在并发竞争的
- ThreadLocalMap中Entry[]数组存储数据,初始化长度16,大于等于3/4阈值,就进行2倍扩容。
- ThreadLocalMap中Entry的key是对ThreadLocal的弱引用,当主线程抛弃掉ThreadLocal对象时,垃圾收集器会忽略这个key的引用而清理掉ThreadLocal对象, 防止了内存泄漏。
- 散列算法-魔数0x61c88647,利用一定算法实现了元素的完美散列
看源码可以得出,set、get、remove操作的都是ThreadLocalMap,key为当前线程,value为线程局部变量缓存值
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
问题
不调用remove会内存溢出吗?
大部分场景下是不会的,少数场景才会。
运行时,会在栈中产生两个引用,指向堆中相应的对象。
可以看到,ThreadLocalMap使用ThreadLocal的弱引用作为key,这样一来,假设当ThreadLocal ref和ThreadLocal之间的强引用断开时,即ThreadLocal ref被置为null,下一次GC时,threadLocal对象势必会被回收。
这样,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,比如使用线程池,线程使用完成之后会被放回线程池中,不会被销毁,这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
参考资料
https://www.cnblogs.com/dennyzhangdd/p/7978455.html ThreadLocal终极源码剖析
https://liwx2000.iteye.com/blog/1774169 ThreadLocal会内存溢出吗
https://www.jianshu.com/p/cdb2ea3792b5 深入理解Java弱引用
__EOF__
欢迎转载,但请注明出处!
欢迎大家一起交流学习!如果有什么疑问,大家可以在评论区一起交流!
如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是我的最大动力!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 互联网不景气了那就玩玩嵌入式吧,用纯.NET开发并制作一个智能桌面机器人(三):用.NET IoT库
· 【非技术】说说2024年我都干了些啥