这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记。
Thread:线程 Local:本地
ThreadLocal:线程本地变量。意思是ThreadLocal中填充的变量是属于当前线程。该变量对于其他线程是隔离的。
简单使用:
结果:
我们可以看见:在thread1 中 设置 threadLocal的值为1。thread1.join让线程1执行结束。此时thread2中get还是null。而且在线程2中set值为2,然后获取,还是2。就说明threadLocal中的值,是线程隔离的。每一个ThreadLocal中的值是属于当前线程。
常用方法介绍:
set(T value): 将此线程局部变量的当前线程副本中的值设置为指定值。
get() : 返回此线程局部变量的当前线程副本中的值。
remove(): 移除此线程局部变量当前线程的值。
ThreadLocal原理:
由上面分析可知,ThreadLocal实际上是在每一个线程里面都维护了一个Map。调用set,get方法就是拿取当前线程的map所以可以保证线程间隔离。
Map结构分析:
map.set()分析
所以综上所述。我们就可以大概的画出ThreadLocal的结构图:
内存泄漏问题:
Entry中的Key为弱引用,有人就觉得内存泄漏与弱引用的key有关系。其实是不对的。分析如下:
弱引用:GC回收ThreadLocal实例 ref会导致Entry中的key为null,但还是存在Thread-> currentThread-> ThreadLocalMap->value这一条引用链,就会key为null的value不能回收。造成内存泄漏
强引用:GC回收ThreadLocal实例 ref,但Entry的key是始终持有ThreadLocal的引用链的,所以threadLocal实例 没办法回收 Entry的key和value也不能回收。所以还是会造成内存泄漏。
所以ThreadLocal造成内存泄漏,与是不是弱引用没有关系。
内存泄漏的根源是:ThreadLocalMap的生命周期和Thread一样,thread不结束就一直会有一条引用链存在,经过JVM的可达性分析,就不会回收Entry。与是不是弱引用没有关系。
如何避免内存泄漏:
1、使用完ThreadLocal,调用remove方法删除对应的Entry
2、使用完ThreadLocal,让当前线程Thread也结束。
第二种方式,不容易控制。而且当使用线程池的时候,线程结束是不会销毁线程的。所以不建议使用。
ThreadLocal提供的remove方法是如何做的:
所以调用remove方法,会先找到当前线程的threadlocalMap里面 的相对应key为当前Threadlocal引用的Entry。清除Entry的引用,然后再清除实际的Entry里面的key和value。都置为null。