- ThreadLocal
- 结构
- Thread -> ThreadLocalMap ->Entry(WeakReference) -> [Key(ThreadLocal):Value(Object)]
- 内存泄漏问题
- ThreadLocal被回收了,ThreadLocalMap的Key没有强引用,但Value还有。首先在非线程池环境不会有长期性的这个问题,因为Thread回收后,ThreadLocal也就被回收了,造成泄漏的情况是: 线程被复用 && ThreadLocal 被回收 && 不再调用ThreadLocal的get set remove 等方法
- 为什么Key不是强引用? 因为若是强引用,当ThreadLocal置为null时,由于ThreadLocalMap还存在对其强引用,则导致无法回收。弱引用的真正目的是回收value
- 为什么不把value设置成弱引用? 因为这样value就总是弱引用,哪怕threadlocalmap在使用时,value也会被回收,和实际场景不符
-
线程池复用问题
- 原因:线程池是复用的,导致提交任务时的线程上下文和执行任务的线程上下文不一致
- 使用TTL解决
- 原理见下方对比
- 使用要点
- 使用完成后调用 remove(),会把 key 置为 null,触发检测所有 key null 的 entry,把 value 置为 null,方便 gc
- 对比
- InheritableThreadLocal
- 原理是在 Thread 构造时,会把调用方线程的inheritableThreadLocals复制到构造的子线程中if (parent.inheritableThreadLocals != null) this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
- TransmittableThreadLocal
- 原理是对runnable即提交的任务进行封装为TtlRunnable,流程是
- 初始化 TtlRunnable 时,对当前线程(提交任务的线程)的TransmittableThreadLocal进行快照(captured())
- 设置到前执行任务的线程中(replay(…))
- 执行任务 (run())
4. 恢复执行线程上下文 restore(backup)
- InheritableThreadLocal
- 应用
- PageHelper 的 pageInfo
- dubbo 的 rpcContext
- 日志 MDC
- Spring 声明式事务(TL 管理 connection)
- 对静态 SimpleDateFormat 优化
- 结构