1.原始的ThreadLocal如何设计的?
原始的ThreadLocal是用线程id作为key的一个hashmap.但是这样会有几个问题.
问题1:当线程变多的时候,这样设计的ThreadLocal会有很多key,最终导致取值效率变低
问题2:由于ThreadLocal将一直引用变量值,即使线程被销毁了,ThreadLocal中的值有可能仍然没有办法进行GC
2.基于以上两个问题,我们可以如何优化呢?
每个Thread都持有一个ThreadLocalMap,这个map使用threadLocal实例作为key,将要存储的值作为值.threadLocal在设置值的时候,直接获取当前的thread并把自身引用和存储的数据put到这个thread中的threadLocalMap中去,取值的时候也直接使用threadLocal实例作为key从当前Thread中的ThreadLocalMap中取到所存储的值.另外,ThreadLocalMap的key使用WeakRefference,这样可以保证threadLocal对象在栈引用销毁后,即使当前线程依然在运行,即使ThreadLocalMap中的Entry依然引用着ThreadLocal实例,这个threadLocal实例依然能够被销毁.并且threadLocal被销毁后,在ThreadLocalMap调用set和getEntry的时候能够销毁threadLocal所对应的值,从而一定程度上避免内存泄漏问题.
3.新的设计方案如何解决#1的两个问题的?
1.就算线程变多,对于一个ThreadLocal对象,每个thread中的key的数量都是1,取值效率大大提高
2.由于现在值是被线程引用,如果线程销毁了,对应所存储的值也将被销毁
image
4.新设计的新问题
1.ThreadLocalMap的key为什么要使用弱引用?
如果ThreadLocal的栈引用被销毁,若ThreadLocalMap的Entry使用的是强引用,则将导致ThreadLocal对象依然不会被销毁,当前ThreadLocalMap将永远不会销毁这个Entry.但是如果使用弱引用,当栈中的ThreadLocal被销毁,则将导致ThreadLocalMap中的key变为null,当前ThreadLocalMap则能够根据key的引用是否为空来决定是否销毁对应的值,从而避免内存泄漏.
2.弱引用真的完全解决内存泄漏了吗?
答案是否定,虽然弱引用机制保证了ThreadLocalRef被销毁后,Entry中的value依然能够被销毁. 但是那个销毁发生在我们调用ThreadLocalMap的set和getEntry的时候,如果我们没有主动调用ThreadLocalMap和set和getEntry,这将仍然导致内存泄漏.
5.使用方式推荐
1.ThreadLocal在使用完后如果没有特殊情况记得要主动调用remove以防止内存泄漏
2.尽量使用private static final ThreadLocal 来定义ThreadLocal对象,使得在同一个线程中一直使用同一个ThreadLocal对象而不用创建多个,但是要注意在线程池中,线程会被复用,如果不调用remove,线程被复用时,前次set的值将仍然存在.
网友评论