ThreadLocal 源码解读

ThreadLocal是什么

ThreadLocal提供了线程的局部变量,每个线程访问独立的变量副本,实现了线程的数据隔离。

ThreadLocal实现原理

每个线程Thread实例中包含两个ThreadLocalMap实例:threadLocalsinheritableThreadLocals,读取ThreadLocal实例时,以ThreadLocal实例为key从当前线程的threadLocals字段读取对应的值。设置值时,向当前线程的threadLocals字段中写入对应的key和value。ThreadLocalMap中的 Entry 使用弱引用引用了ThreadLocal,并强引用了ThreadLocal对应的线程本地变量副本。当 ThreadLocal 变量被回收后,该映射对应的键变为 null,ThreadLocalMap中对应的 Entry 无法被手动移除,进而使得线程的变量副本被该 Entry引用而无法被回收造成内存泄漏。

ThreadLocalMap与内存泄漏

ThreadLocalMap 的每个 Entry 都是一个对键(ThreadLocal)的弱引用,同时 Entry 又包含了一个对值(当前线程的变量副本)的强引用。当没有强引用指向 ThreadLocal 变量时,ThreadLocal可被回收,但Entry中包含的变量副本不能自动释放。

防止ThreadLocal内存泄漏

ThreadLocalMap 的 set 方法中,通过 replaceStaleEntry 方法将所有键为 null 的 Entry 的值设置为 null,从而使得该值可被回收。另外,会在 remove方法、rehash 方法中通过 expungeStaleEntry 方法将键为 null 的 Entry 的值设为null并将Entry设置为 null 从而使得该 Entry 及对应的值可被回收。通过这种方式,ThreadLocal 可防止内存泄漏。

为了及时的内存回收,可以调用ThreadLocal.remove()方法主动移除对应的Entry及线程变量副本。

总结

  1. ThreadLocal 并不解决线程间共享数据的问题
  2. ThreadLocal 通过隐式的在不同线程内创建独立实例副本避免了实例线程安全的问题
  3. 每个线程持有一个 Map 并维护了 ThreadLocal 对象与具体实例的映射,该 Map 由于只被持有它的线程访问,故不存在线程安全以及锁的问题
  4. ThreadLocalMap 的 Entry 对 ThreadLocal 的引用为弱引用,避免了 ThreadLocal 对象无法被回收的问题
  5. ThreadLocalMap 的 set 方法通过调用 replaceStaleEntry 方法回收键为 null 的 Entry 对象的值(即为具体实例)以及 Entry 对象本身从而防止内存泄漏
  6. ThreadLocal 适用于变量在线程间隔离但在方法间共享的场景

ThreadLocal使用案例

  1. DataSource中的getConnection()方法
  2. Spring Security中的ThreadLocalSecurityContextHolderStrategy

你可能感兴趣的:(ThreadLocal 源码解读)