ThreadLocal系列源码分析
ThreadLocal 源码分析:
直接看 ThreadLocal##get() 方法:
1 | class ThreadLocal { |
再看 ThreadLocal##set()
1 | class ThreadLocal{ |
每个线程都保存自己的一份 ThreadLocal 表,这个表长啥样?
1 | class Thread { |
WeakReference<ThreadLocal<?>> 表示只有一个 ThreadLocal 引用,但是这个引用比较特殊
ThreadLocal 为什么要使用 WeakReference?
当发生 GC 时,WeakReference 中持有的引用便会被回收,这样的意义是:当 ThreadLocal 置为 Null 时,Thread 中的 map 自动进行回收(只有 map 中的 key 会自动回收)。
WeakReference 应用场景?
- map 自动回收
- 缓存自动释放
WeakReference 内的引用如果被回收了,自身如何存在?
自身不会被回收,但是会被放到 ReferenceQuene 中(如果有初始化),发生回收后,可以遍历 ReferenceQuene 来进行后续操作。
feign 中使用 RequestInterceptor 遇到的一个问题:
当 Hystrix资源隔离策略 为线程模式时,RequestInterceptor 会变成异步执行,这时候 RequestContextHolder.getRequestAttributes() 拿到的结果会为 Null ,这是因为 RequestContextHolder 基于 ThreadLocal。
如何解决上述问题?
使用 InheritableThreadLocal ,该类可以实现父线程的 ThreadLocalMap 传递到子线程的 ThreadLocalMap中。
InheritableThreadLocal 源码分析
ThreadLocalMap 的传递发生在新线程创建时,那么可以从 Thread 的初始化方法入手:
1 | class Thread { |
再看 ThreadLocal##createInheritedMap()
1 | class ThreadLocal { |
ThreadLocal 中 不支持 传递方法 childValue(),该方法由子类 InheritableThreadLocal 重写:
1 | public class InheritableThreadLocal<T> extends ThreadLocal<T> { |
InheritableThreadLocal 有什么不足?
InheritableThreadLocal 只能做到父线程到子线程的传递,但目前往往会使用线程池,这时候 InheritableThreadLocal 就失效了。
可以使用 TransmittableThreadLocal 解决上述问题,TransmittableThreadLocal 的任务是把 任务提交给线程池时的ThreadLocal
值传递到 任务执行时。
使用案例:
1 | TransmittableThreadLocal<String> context = new TransmittableThreadLocal<String>(); |
那么来看看 TtlRunnable##get 中干了些啥
TransmittableThreadLocal 源码分析:
1 | class TtlRunnable implements Runnable, TtlEnhanced { |
包装时核心的方法来自 TransmittableThreadLocal 中的 静态内部类 Transmitter##capture()
1 | public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> { |
holder 是啥?
1 | // TransmittableThreadLocal 继承自 InheritableThreadLocal |
holder 中数据的相关操作:
1 | public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> { |
- 在线程A中写 TransmittableThreadLocal 时,便会将值放到 holder 的 map 中,holder 是一个ThreadLocal,也就是线程A持有的
- 在线程A中创建一个任务时,将 holder 保存的值保存到任务中,随任务一起传递到线程B中
- 在线程B中执行 TtlRunnable##run()
可以看到 TtlRunnable 是把原任务做了一个包装,那么本身也是作为 Runnable,那么最关键的就是 run() 方法:
1 | class TtlRunnable implements Runnable, TtlEnhanced { |
核心方法 Transmitter##replay():
1 | public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> { |
TransmittableThreadLocal 流程总结:
- 在线程A中写 TransmittableThreadLocal 时,便会将值放到 holder 的 map 中,holder 是一个ThreadLocal,也就是线程A持有的
- 在线程A中创建一个任务时,使用TtlRunnable做一层包装,将 holder 保存的值保存到任务中,随任务一起传递到线程B中
- 在线程B中执行 TtlRunnable##run()
- 执行前,使用传递来的 ThreadLocalMap,覆盖本地的 ThreadLocalMap
- 执行Runnable##run()
- 执行后,恢复本地的 ThreadLocalMap
流程图:
总结
- ThreadLocal 根本问题是由于这是一个线程无关的变量,而传递的本质是使用一个线程共享的变量进行传递值,因为线程共享,所以需要注意 线程安全问题
- TransmittableThreadLocal 继承了 InheritableThreadLocal,因此在第一次被创建时,还是使用的 InheritableThreadLocal 的功能,而后在线程池中,从父线程中继承的 ThreadLocalMap,便是作为本地元素 ThreadLocalMap 被备份和恢复。