一.ThreadLocal的作用
ThreadLocal提供的实际上就是一种线程隔离,不同的线程的变量归属不同的线程,线程之间不能想问访问各自的变量,当线程生命周期结束的时候,线程会被销毁。
二.ThreadLocal的原理
Thread类中有一个成员变量threadLocals:
这个ThreadLocalMap 是ThreadLocal类中一个静态内部类;
ThreadLocal类中最主要的就是set 方法get方法;
set方法
这个getMap(t); 传入一个Thread对象返回一个 ThreadLocalMap 内部类
ThreadLocal类中
在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
Thread类中
ThreadLocal类中
如果Thread类中threadLocals为空就是这个map为空的时候:
那么就会把在当前线程的副本中存储要存储的值firstValue和当前ThreadLoal 变量给Thread类中
这样就相当于ThreadLoal 在不同的 Thread 中有不同的副本。
ThreadLocal类中
ThreadLocal 适用于每个线程需要自己独立的实例且该实例需要在多个方法中被使用,也即变量在线程间隔离而在方法或类间共享的场景。后文会通过实例详细阐述该观点。另外,该场景下,并非必须使用 ThreadLocal ,其它方式完全可以实现同样的效果,只是 ThreadLocal 使得实现更简洁。
三.ThreadLocal内存泄露
ThreadLocalMap
由这个构造函数,我们可以画出ThreadLocal所用的map结构。
图中,黄线代表弱引用。我们挑出这两个重要的引用,别的不用特别看。需要注意的是,当弱引用作为不可达对象的时候,会在下一次GC的时候会被回收。Entry是继承了弱引用的。脑子里先有这个大体架构。
Entry
Entry set方法
总结:
1.也就是说 ThreadLocal
本身并不存储值,它只是作为一个 key 来让线程从 ThreadLocalMap
获取 value。值得注意的是图中的虚线,表示 ThreadLocalMap
是使用 ThreadLocal
的弱引用作为 Key 的,弱引用的对象在 GC 时会被回收。
2.ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value
永远无法回收,造成内存泄漏。
3.ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocal的get(),set(),remove()
的时候都会清除线程ThreadLocalMap里所有key为null的value。
4.但是这些被动的预防措施并不能保证不会内存泄漏:
(1)使用static的ThreadLocal,延长了ThreadLocal的生命周期,可能导致内存泄漏。
(2)分配使用了ThreadLocal又不再调用get(),set(),remove()方法,那么就会导致内存泄漏,因为这块内存一直存在。