注:本专栏文章均为本人原创,未经本人授权请勿私自转载,谢谢。
ThreadLocal 原理
在 JVM 中,每个线程都存在一个 ThreadLocalMap<ThreadLocal, V> ,存储着当前线程的所有 ThreadLocal 对象映射。
以下为对 ThreadLocal 进行取值的流程(假定对象 A 中引用的 ThreadLocal 对象 B):
- 通过获取当前线程信息,可以获取到当前线程的 ThreadLocalMap。
- 然后通过对象 A 的引用地址,在 ThreadLocalMap 中寻找自己对应的值。
为何 ThreadLocalMap 的 Entry 要使用弱引用?
因为引用 ThreadLocal 的变量有两种类型:一类是类似于对象 A 的业务对象,一类则是线程对象(ThreadLocalMap) 。
而当业务对象所有引用都没有了的时候,该 ThreadLocal 对象的使命其实也就结束了。
此时,由于 ThreadLocalMap 中的 key ThreadLocal 为弱引用,可以直接被垃圾回收掉。
为何会引起内存泄漏?
由上可知,ThreadLocalMap 中的 key 由于为弱引用,在无业务对象时会被回收,而 value 则一直存在,导致内存泄漏。
在调用 ThreadLocal 的相关方法时,会对 ThreadLocalMap 中 key 为 null 的值进行清理,实际内存泄漏并不严重。
FastThreadLocal 原理
在 Netty 的 FastThreadLocalThread 线程中,每个线程都存在一个 InternalThreadLocalMap 存储着当前线程的所有 ThreadLocal 对象映射。
以下为对 FastThreadLocal 进行取值的流程(假定对象 A 中引用的 ThreadLocal 对象 B):
- InternalThreadLocalMap 中有一个 indexedVariables 数组用于存储各个 ThreadLocal 变量。同时有一个静态 AtomicInteger 变量用于并发递增索引。
- 在 ThreadLocal 对象 B 初始化时会调用 InternalThreadLocalMap 的 nextVariableIndex() 方法申请递增索引,并在当前对象 B 中使用静态变量记录该索引,此时 ThreadLocal 对象在每个线程的 InternalThreadLocalMap 中的位置都为该索引值。
- 在 ThreadLocal 对象 B 进行取值时,只需读取对象 B 中保存的静态索引,去当前线程的 InternalThreadLocalMap 该索引位置处即可读出该值。