早期版本中:
- ThreadLocalMap是ThreadLocal的静态内部类,ThreadLocalMap的所有者是ThreadLocal。
- ThreadLocalMap表中Entry的key为Thread实例。
所以就类似于ThreadLocal维护了所有线程的本地变量。但是这样会导致,
ThreadLocalMap中有大量Entry。
因为ThreadLocal是强引用,所以如果删除线程,对应的ThreadLocalMap不会被删除。导致内存挤压。
因此在JDK1.8之后:
- ThreadLocalMap的所有者改为Thread。每一个Thread实例拥有一个ThreadLocalMap对象。
因为每一个Thread实例拥有一个ThreadLocalMap对象,所以如果删除线程,对应的ThreadLocalMap会随之销毁。同时也减少了ThreadLocalMap中Entry的数量。
至此早期版本存在的问题得到解决。下面是弱引用的问题。
- ThreadLocalMap表中Entry的key为ThreadLocal实例,由ThreadLocal向map获取和设置线程变量值。(见下图,来源:极客时间)
因为ThreadLocal是Java对象,存放在堆中。所以Thread和ThreadLocalMap访问ThreadLocal是强引用。如果Thread不访问ThreadLocal对象,则强引用消失。
value是存储的局部变量,根据存储需求变化,由用户而不由程序员控制。所以在下一次垃圾回收时 key 会被清理掉,而 value 不会被清理,value 的回收取决于 Thread 对象的生命周期。
如果是ThreadLocalMap的Entry是强引用:
那么,在Thread不访问ThreadLocal对象之后,在下一次垃圾回收时,对应的ThreadLocal对象依然存在。
如果是ThreadLocalMap的Entry是弱引用:
那么,在Thread不访问ThreadLocal对象之后,在下一次垃圾回收时,对应的ThreadLocal对象会被自动清理。
但是,value不会随着ThreadLocal对象清理而被销毁。所以需要及时的remove()操作来减少空间浪费。
不过,这就意味着使用完 ThreadLocal , CurrentThread 依然运行的前提下。就算忘记调用 remove 方法,弱引用比强引用可以多一层保障:弱引用的 ThreadLocal 会被回收.对应value在下一次 ThreadLocaIMap 调用 set/get/remove 中的任一方法的时候会被清除,从而避免内存泄漏。
参考资料:
https://blog.csdn.net/bbscz007/article/details/105686382
黑马程序员讲ThreadLocal(2020年)
极客时间JUC入门与进阶专栏