先说结论
一个Thread对应一个ThreadLocalMap,一个ThreadLocalMap可保存多个ThreadLocal为键的键值对,值为需要线程共享的数据,而且多个线程可共用一个ThreadLocal对象,在web项目中多有这样的设计。
源码分析
Thread中有一个ThreadLocalMap类型的属性threadlocals,ThreadLocalMap是ThreadLocal的一个内部类。
1.ThreadLocal的set方法
先获取当前线程t,再执行getMap(t)
其实就是获取t.threadLocals,若此时getMap(t)为空,则执行createMap(this,value)
若为空,则创建ThreadMap对象并给当前Thread的threadLocals属性赋值,并在ThreadLocalMap中放入一对键值对,其中键为当前调用方法的ThreaadLocal对象(this),值为set方法传入的value。
若非空,则直接在该线程对应的ThreadLocalMap中放入该键值对即可。
2.ThreadLocal的get方法
与set方法类似,先获取当前Thread对应的ThreadLocalMap,再通过ThreadLocal实例(this)获取所需值。
3.remove方法
4.ThreadLocal的并发性
由于每一个ThreadLocal都对应了一个ThreadLocalMap,也就是说每一个线程都持有一份数据副本,也就不存在数据的并发操作。
Thread中还有一个ThreadLocalMap类型的属性inheritableThreadLocals。在new Thread()时,在构造方法里会把父线程中的inheritableThreadLocals的ThreadLocalMap内容复制给当前子线程的inheritableThreadLocals,也就是说子线程会继承父线程中的inheritableThreadLocals对应的ThreadLocalMap,而且是复制一份,子线程中的ThreadLocalMap和父线程中的ThreadLocalMap中的内容一开始是一样的,但是后续的修改将互不影响。
5.ThreadLocal的内存泄漏问题
ThreadLocaMap中,key对ThreadLocal的引用时弱引用。
5.1 为什么要对ThreadLocal使用弱引用?
因为ThreadLocal就是做同线程数据共享的,若ThreadLocal中不存在对象引用时,ThreadLocal也应该被回收,而由于设计的引用链,Thread引用ThreadLocalMap,ThreadLocalMap中的key又引用ThreadLocal,使得ThreadLocal不会被回收。而弱引用的特点为当对象只有弱引用存在时,该对象可以被gc回收。所以对Thread Local使用弱引用能解决此问题。
5.2 引发的内存泄漏问题
ThreadLocal为弱引用,这样就会导致ThreadLocalMap.Entry的key为null,而value是强引用,那么会导致value引用的对象只有在Thread销毁时才会被释放,ThreadLocalMap中也永远存在一个value非空的永远不会被访问到的Entry,引发了内存的泄漏。
- 解决方法:
程序员手动执行remove方法。
5.3 为什么不把value设置为弱引用?
避免外部持有key的引用来查询value,而value却被回收的情况。
标签:Thread,ThreadLocalMap,value,ThreadLocal,线程,引用 From: https://www.cnblogs.com/zlzw1/p/18377198