ThreadLocal | 青训营笔记

57 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记。

Thread:线程 Local:本地

ThreadLocal:线程本地变量。意思是ThreadLocal中填充的变量是属于当前线程。该变量对于其他线程是隔离的。

简单使用:

image.png

结果:

image.png 我们可以看见:在thread1 中 设置 threadLocal的值为1。thread1.join让线程1执行结束。此时thread2中get还是null。而且在线程2中set值为2,然后获取,还是2。就说明threadLocal中的值,是线程隔离的。每一个ThreadLocal中的值是属于当前线程。

常用方法介绍:

set(T value): 将此线程局部变量的当前线程副本中的值设置为指定值。

get() : 返回此线程局部变量的当前线程副本中的值。

remove(): 移除此线程局部变量当前线程的值。

ThreadLocal原理:

image.png

由上面分析可知,ThreadLocal实际上是在每一个线程里面都维护了一个Map。调用set,get方法就是拿取当前线程的map所以可以保证线程间隔离。

Map结构分析:

image.png

map.set()分析

image.png

所以综上所述。我们就可以大概的画出ThreadLocal的结构图:

image.png

image.png


内存泄漏问题:

image.png

Entry中的Key为弱引用,有人就觉得内存泄漏与弱引用的key有关系。其实是不对的。分析如下:

image.png

弱引用: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方法是如何做的:

image.png

image.png

image.png

所以调用remove方法,会先找到当前线程的threadlocalMap里面 的相对应key为当前Threadlocal引用的Entry。清除Entry的引用,然后再清除实际的Entry里面的key和value。都置为null。