ThreadLocal
1. 简介
ThreadLocal是Thread的局部变量,用于编写多线程程序,这种变量在多线程环境下访问时能够保证各个线程里变量的独立性。
能干什么:
- 存储数据 & 线程间数据隔离
- 在进行对象间跨层传递的时候,使用ThreadLocal可以避免多次传递。比如将用户信息
set
到ThreadLocal中,当前线程在任何地方都可以通过ThreadLocal对象直接get
用户信息,不用为了获取用户信息而把用户信息传来传去
2. 初探
既然说 ThreadLocal 是线程的局部变量
,那么一起验证下以下两个问题:
- 主线程中set的
Hello World ~
,在thread-1线程中能否get到 - 在thread-1线程中设置的值能否在主线程中get到 & thread-1线程set的值会不会影响主线程中set的结果
运行打印结果如下:
小结
通过日志结果可以判断出:主线程与thread-1线程set 和 get 值都互不影响
3. 源码
在看源码之前我们先大概了解一下Thread,ThreadLocal,ThreadLocalMap,Entry都是什么关系?
3.1 ThreadLocal
ThreadLocal类其实很简单,核心方法就set,get,remove
withInitial:这个方法采用Lambda方式传入实现了 Supplier 函数接口的参数。
3.1.1 set()
哈? 超简单有没有。
- 获取当前线程
- 获取Map
- map不为空就set,为空就create
首先看下getMap
方法:
昂~ 就是把线程对象threadLocals
属性返回去了
等等 🤔,为什么是线程对象的threadLocals
属性呢?
看到这里应该就有点明白了,原来ThreadLocal set值的时候操作的是Thread对象中的threadLocals
,就说为什么线程间数据隔离呢,原来值都存储在了线程对象自己的成员变量中。
我们继续看下createMap(t, value)
具体干了些什么
创建了一个ThreadLocalMap
对象赋值给了线程的threadLocals
属性
小结:
ThreadLocal::set
方法逻辑还是比较清晰的
- 获取当前线程
- 获取当前线程的
threadLocals
属性值作为操作的map - map不为空就set,为空就create一个
ThreadLocalMap
对象赋值给threadLocals
这里先不纠结
ThreadLocalMap
是个什么东东,就当它是个map
3.1.2 get()
- 获取当前线程 & 返回当前线程的
threadLocals
作为map - map不为空就根据this(当前的ThreadLocal实例对象)获取到Map的Entry对象
- 如果Entry不为null就返回
这么一看get方法也没啥
3.1.3 remove()
这?就没啥好说的了
3.2 ThreadLocalMap
在看ThreadLocal方法时 反复提到了ThreadLocalMap
对象 并且 对象方法的操作其实都是操作的该对象
(Thread --> ThreadLocal.ThreadLocalMap threadLocals = null;
),它是个什么呢?
ThreadLocalMap 其实是ThreadLocal的一个内部静态类:
3.2.1 field
3.2.2 Constructor
ThreadLocalMap 构造方法源码如下:
创建ThreadLocalMap对象时其实内部是创建了一个Entry对象:new Entry(firstKey, firstValue)
- firstKey:ThreadLocal对象
- firstValue: ThreadLocal set的时候传入的值
重新整理一下链路:
-
ThreadLocal set()
-
set 方法中首先获取到当前线程
-
获取当前线程的
threadLocals
值 -
首次进来
threadLocals
值为null
-
-
createMap()
- new ThreadLocal(ThreadLocal,Object)
-
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue)
- new Entry(ThreadLocal<?> k, Object v)
3.2.3 getEntry()
getEntry()时使用的是ThreadLocal实例作为key计算的数组index
3.2.4 set()
3.2.5 remove()
3.3 Entry
Entry 其实 是 ThreadLocalMap 的内部静态类。好一个套娃~ ThreadLocal ---> ThreadLocalMap ---> Entry
弱引用就是只要发生垃圾回收弱引用指向的对象就会被回收。
这里 Entry
对象继承了 WeakReference
,通过super(k)
可以了解到弱引用指向了ThreadLocal
实例,大概关系是:
4. 内存分布
-
假设随着 Stack 的执行结束,ThreadLocal Ref 被GC回收了(假设使用的是线程池中的Thread对象)。
-
ThreadLocalMap 中的 key只持有ThreadLocal的弱引用,没有任何强引用指向threadlocal实例, 所以threadlocal就可以顺利被gc回收,此时Entry中的key=null。
-
没有手动删除这个Entry以及CurrentThread依然运行的前提下,也存在有强引用链 threadRef-->currentThread-->threadLocalMap-->entry --> value ,value不会被回收, 而这块value永远不会被访问到了,导致value内存泄漏(常驻内存)。
原因
1.没有良好的编程思想,使用完未释放。
2.Thread对象 生命周期太长了。
参考:
https://blog.csdn.net/weixin_44075132/article/details/115543608
__EOF__

本文链接:https://www.cnblogs.com/ludangxin/p/16537390.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!