ThreadLocal 线程池上下文传递
ThreadLocal 线程池上下文传递:从 InheritableThreadLocal 缺陷到 TransmittableThreadLocal 全解析 🤔 一、JDK 设计者为什么要给每个线程配一个"私房钱罐" 多线程编程中有一个经典矛盾:线程之间要共享一部分数据来协作,又要有各自私有的数据来隔离。共享数据靠锁来保护,私有数据呢?如果每建一个新线程都要手动传参数、写包装类,代码很快就变成意大利面条。 JDK 1.2 的设计者(Josh Bloch 等人)给出的方案是 ThreadLocal——每个线程维护一个私有的 ThreadLocalMap,key 是 ThreadLocal 实例,value 是你想隔离的数据。同一个 ThreadLocal 对象在不同线程中的值互不干扰。这个设计让"线程级上下文"(traceId、事务、用户 Session)变得自然:只要在主线程 set 一下,当前线程的任何方法都能 get 到,不需要在方法签名里一路传参。 但 JDK 设计者很快发现一个新问题:ThreadLocal 在线程间是完全隔离的——如果父线程 set 了值,新建子线程时,子线程拿不到。这就是为什么后来又有了 InheritableThreadLocal:它在 Thread 构造函数中触发 init(),将父线程 ThreadLocalMap 中标记为可继承的条目浅拷贝到子线程。 然而,ITL 的设计有一个致命缺陷——它只在 new Thread() 时触发传递。线程池复用已有线程,不再走 Thread 构造函数,ITL 的传递逻辑完全不执行。第一次提交任务时碰巧用的是刚创建的新线程(触发了一次传递),第二次复用同一个线程时,父线程的新值就传不过来了。 这就是阿里开源的 TransmittableThreadLocal 要解决的问题。它的核心思路是:不再依赖线程创建时的一次性传递,而是在每次提交任务时主动 capture 父线程的快照 → replay 到工作线程 → 任务完成后再 restore 还原。 阅读本篇文章的收获: InheritableThreadLocal 是如何在 Thread 构造函数中实现传递的?源码在哪一行触发? 为什么它在线程池中会失效?根源在 JDK 源码的哪一行? TransmittableThreadLocal 又是如何在源码层面解决这些缺陷的? capture() / replay() / restore() 三个方法各自做了什么? 🧵 二、ThreadLocal 基础回顾:数据到底存在哪里 在深入 ITL 和 TTL 之前,先快速回顾 ThreadLocal 的核心数据结构。如果你已经熟悉这部分,可以直接跳到第三章。 ...