1.目的
ThreadLocal目的是保存一些线程级别的全局变量,比如connection,或者事务上下文,避免这些值需要一直通过函数参数的方式一路传递。
2. 常见用法
3.实现分析
要实现上面这样的功能,最简单的想法是用一个Map<Thread,T>,如下:
这样也能实现ThreadLocal的效果,但是有一个问题,当对应的线程消失后,map中对应的线程值并不会被回收,从而造成内存泄露。
事实上ThreadLocal是这样做的:
注意这里如果取到没有该线程对应的值,会调用setInitialValue();,最终调用initialValue()生成一个值,这也是我们很多场景下要override这个方法的原因;
下面看一下getMap(Thread t)方法:
在Thread类中:
由此可见,所有的ThreadLocal的信息,最终是关联到Thread上的,线程消失后,对应的Thread对象也被回收,这时对应的ThreadLocal对象也会被回收。
这里为什么是一个ThreadLocalMap呢,因为一个线程可以有多个ThreadLocal变量,通过map.getEntry(this)取得对应的某个具体的变量。
最后要注意的一点是,ThreadLocalMap的Entry是一个weakReference:
这里主要因为ThreadLocalMap的key是ThreadLocal对象,如果某个ThreadLocal对象所有的强引用没有了,不能因为ThreadLocalMap的引用导致他不能被回收。注意,gc后entry的key.get()结果为null,但是回收实在后续插入元素时触发的。
附:
这里补充一下weakReference的用法供参考(当强引用不存在时,下次垃圾回收会回收弱引用所引用的对象):
结果输出:
4.1 为什么一般的ThreadLocal用法都要加static,如下:
class Test { private static final ThreadLocal<String> globalName = new ThreadLocal<String>(); }
class TestThreadLocal{ public static void main(String[] args) { Test t1 = new Test(); Test t2 = new Test(); t1.pool.set("a"); System.out.println(t1.pool.get()); System.out.println(t2.pool.get()); } } class Test{ public ThreadLocal pool = new ThreadLocal(); }
输出将会是:a,null
原因就无需多解释了。唯一需要啰嗦的一点是,就算一般情况都是单例,上面那个weakreference还是必要的,因为作为框架代码,不能保证正常使用的情况下一个线程有很多ThreadLocal,如果不用weakreference,就会有内存泄漏的风险,特别是针对线程池中的线程。
参考: