ThreadLocal与FastThreadLocal

129 阅读2分钟

注:本专栏文章均为本人原创,未经本人授权请勿私自转载,谢谢。

ThreadLocal 原理

在 JVM 中,每个线程都存在一个 ThreadLocalMap<ThreadLocal, V> ,存储着当前线程的所有 ThreadLocal 对象映射

以下为对 ThreadLocal 进行取值的流程(假定对象 A 中引用的 ThreadLocal 对象 B):

  1. 通过获取当前线程信息,可以获取到当前线程的 ThreadLocalMap。
  2. 然后通过对象 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):

  1. InternalThreadLocalMap 中有一个 indexedVariables 数组用于存储各个 ThreadLocal 变量。同时有一个静态 AtomicInteger 变量用于并发递增索引。
  2. 在 ThreadLocal 对象 B 初始化时会调用 InternalThreadLocalMap 的 nextVariableIndex() 方法申请递增索引,并在当前对象 B 中使用静态变量记录该索引,此时 ThreadLocal 对象在每个线程的 InternalThreadLocalMap 中的位置都为该索引值。
  3. 在 ThreadLocal 对象 B 进行取值时,只需读取对象 B 中保存的静态索引,去当前线程的 InternalThreadLocalMap 该索引位置处即可读出该值。