首先我们来看一下这个
猜猜输出的是什么?结果一样吗?
好了我们看
结果不一样。为什么呢?
我们现在一个类Thread类,发现Thread中维护了一个ThreadLocal.ThreadLocalMap类型的字段,换言之,只要创建了不同的线程那么就会存在多个ThreadLocalMap即不同的线程维护了不同的变量副本
回头来看看我们在使用ThreadLocal实例变量调用其set方法时的源码如下
调用set方法的时候,会获取当前线程对象 ,然后从获取到的线程对象实例中拿到对应的ThreadLocalMap,将当前实例对象(调用set方法的ThreadLocal实例)作为ThreadLocalMap的key,传入的值作为ThreadLocalMap的value存入线程变量副本(map)中,那么就会有以下几种情况:
1.当存在多个线程时,即存在多个ThreadLocalMap(线程变量副本),即使是同一个ThreadLocal实例,即ThreadLocalMap的this是同一个,那也不会产生数据覆盖,因为ThreadLocalMap不是同一个,好比将同一瓶水放入不同的水杯
2.在一个线程内,创建多个ThreadLocal实例,也不会产生数据覆盖问题,虽然ThreadLocalMap是同一个但map中的key不同
总结一下就是不同的线程,拿到ThreadLocal.ThreadLocalMap类型的threadLocals字段不一样,获取ThreadLocalMap也就不一样。
即使是同一个ThreadLocal对象的实例去调set方法,出来的结果也就不会被覆盖。
同理,如果同一个线程内,但是ThreadLocal对象的实例不同,那么结果也不会覆盖
ThreadLocalMap里面是一个又一个的Entry,Entry里面是一个key-value结构,Entry里面的key和value就是set方法里的this和value,
同时Entry由于继承了WeakReference这个类,所以Entry是弱引用对象
Entry是弱引用对象,当ThreadLocal对象的实例,就是set方法里的this没有被引用,也就是Entry里的key,就会被gc回收。但是刚才说了Entry它是放在ThreadLocalMap里面的(ThreadLocalMap和Entry是强引用),ThreadLocalMap它是在线程里的,线程不结束,即使Entry的key被回收了,它也依然存在,也就是它里面的value依旧存在。
随着程序的运行,久了之后可能ThreadLocalMap的值会越来越多就会出现内存泄漏的问题,怎么解决呢?
就是在使用完线程共享变量后,显式调用ThreadLocalMap.remove方法清除线程共享变量,这样就是手动切断ThreadLocalMap与Entry之间的引用关系,然后就可以被回收了
图例:
当存储一个键值对后,ThreadLocalMap的key是ThreadLocal对象,value是存储的变量,他们通过Entry弱引用关联
当我们不使用ThreadLocal对象的时候,threadlocal=null;由于是弱引用,那么在垃圾回收后,ThreadLocal对象可以被回收 。