关于ThreadLocal的探究

起因

ThreadLocal,第一次看到这个还以为是Thread的一个子类,经常在各种面试题中看到它的身影,就去仔细看了一下,整理成文档写下来,方便以后复习,同时也方便有需要的人查阅,如有错误之处,请指出,TKS。

用途

synchronized,这个单词相信大家都不会感到陌生,因为这是Java的重量级同步锁,用来解决多线程变量同步问题,但是效率并不高,因为会有阻塞等待,是典型的时间换空间算法。而与之对应的就是ThreadLocal,用来解决多线程变量隔离问题,即每个线程都保存一个副本供自己使用而不会对其他线程的变量产生影响。

问题分析

经常会被问到的一个问题就是ThreadLocal关于内存泄露的原因。

通过阅读源码,可以看到ThreadLocal.get会调用当前线程的ThreadLocalMap,ThreadLocalMap看上去是一个Map实际上是一个数组,数组里面存放的是Entry实体,数组中的位置由hash算法确定。hash算法就会产生hash冲突,解决冲突的原因一般包含两种:开放地址法和拉链法。HashMap就是用的拉链法将冲突的节点以单链表形式挂在已有节点后面,这里就不细说,有兴趣的自己去看HashMap的源码。ThreadLocalMap 中使用开放地址法来处理散列冲突,因为在 ThreadLocalMap 中的散列值分散的十分均匀,很少会出现冲突,而且 ThreadLocalMap 经常需要清除无用的对象,使用纯数组更加方便。

Entry继承自WeakReference<ThreadLocal>,即作为一个弱引用实现,包含两个属性key、value。其中key是以ThreadLocal实体变量作为key,存放的值作为value。因为弱引用只存在于key上,所以key会被回收. 而value还存在着强引用.只有thead退出以后,value的强引用链条才会断掉,否则就没有办法访问这些key为null的Entry的value,同时ThreadLocalMap长时间被持有,Thread -> ThreaLocalMap -> Entry -> value导致永远无法回收,造成内存泄漏。

在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。(是通过检查key值如果为空,调用expungeStaleEntry)

如何解决

解决是不可能的,我就是从这跳下去都解决不了。

1.官方建议使用private static 修饰ThreadLocal延长其生命周期,减少出现问题的几率。

2.用完就remove掉,这才是根本。

为什么要用弱引用

看上去都是因为弱引用导致的问题,那么为什么要用弱引用,直接强引用不就ojbk?

实际上泄露的关键是你线程太久不结束并且你不remove,而不是因为弱引用,就算换成强引用该泄露还是得泄露。弱引用只是帮助你降低ThreadLocal这个对象持有造成的泄露风险(解决“1w个线程在用ThreadLocal,ThreadLocal一辈子都回收不了”)。

猜你喜欢

转载自blog.csdn.net/tian2342/article/details/81113693