ThreadLocal
ThreadLocal
ThreadLocal的实现原理
如果你创建了一个ThreadLocal
变量,那么访问这个变量的每个线程都会有这个变量的本地副本,这也是ThreadLocal
变量名的由来。他们可以使用 get()
和 set()
方法来获取默认值或将其值更改为当前线程所存的副本的值,从而避免了线程安全问题。
最终的变量是放在了当前线程的 ThreadLocalMap
中,并不是存在 ThreadLocal
上,ThreadLocal
可以理解为只是ThreadLocalMap
的封装,传递了变量值。 ThrealLocal
类中可以通过Thread.currentThread()
获取到当前线程对象后,直接通过getMap(Thread t)
可以访问到该线程的ThreadLocalMap
对象。
每个Thread
中都具备一个ThreadLocalMap
,而ThreadLocalMap
可以存储以ThreadLocal
为 key ,Object
对象为 value 的键值对。
- 每个
Thread
中都存储着一个成员变量ThreadLocalMap
ThreadLocal
本身不存储数据,像是一个工具类,基于ThreadLocal
去操作ThreadLocalMap
ThreadLocalMap
本身就是基于Entry[]
实现的,因为一个线程可以绑定多个ThreadLocal
,这样一来,可能需要存储多个数据,所以采用Entry[]
的形式实现。
每一个线程都自己独立的ThreadLocalMap
,再基于ThreadLocal
对象本身作为key,对value进行存取。
ThreadLocalMap
的key是一个弱引用,弱引用的特点是,即便有弱引用,在GC时,也必须被回收。在ThreadLocal
对象失去引用后,如果key的引用是强引用,会导致ThreadLocal
对象无法被回收。
ThreadLocal的内存泄露问题
ThreadLocalMap
中使用的 key 为 ThreadLocal
的弱引用,而 value 是强引用。所以,如果 ThreadLocal
没有被外部强引用的情况下,在垃圾回收的时候,key 会被清理掉,而 value 不会被清理掉。
这样一来,ThreadLocalMap
中就会出现 key 为 null 的 Entry。假如我们不做任何措施的话,value 永远无法被 GC 回收,这个时候就可能会产生内存泄露。ThreadLocalMap
实现中已经考虑了这种情况,在调用 set()
、get()
、remove()
方法的时候,会清理掉 key 为 null 的记录。使用完 ThreadLocal
方法后最好手动调用remove()
方法。
如果ThreadLocal
引用丢失,key因为弱引用会被GC回收掉,如果同时线程还没有被回收,就会导致内存泄漏,内存中的value无法被回收,同时也无法被获取到。
只需要在使用完毕ThreadLocal
对象之后,及时的调用remove
方法,移除Entry即可。
ThreadLocal的使用场景
使用场景一:代替参数的显式传递(使用较少)
使用场景二:全局存储用户信息(项目中用到)
ThreadLocal 用作每个线程内需要独立保存信息,以便供其他方法更方便地获取该信息的场景。每个线程获取到的信息可能都是不一样的,前面执行的方法保存了信息后,后续方法可以通过ThreadLocal 直接获取到,避免了传参
使用场景三:解决线程安全问题
ThreadLocal 用作保存每个线程独享的对象,为每个线程都创建一个副本,这样每个线程都可以修改自己所拥有的副本, 而不会影响其他线程的副本,确保了线程安全。
参考:
ThreadLocal源码分析
InheritableThreadLocal
TransmittableThreadLocal
参考:
- alibaba/transmittable-thread-local: 📌 a missing Java std lib(simple & 0-dependency) for framework/middleware, provide an enhanced InheritableThreadLocal that transmits values between threads even using thread pooling components.
- 小伙伴同学们写的 TTL实际业务使用场景 与 设计实现解析的文章(写得都很好! )❤️ · Issue #123 · alibaba/transmittable-thread-local