Skip to content

面试速答(先看这里)

**一句话结论:**InheritableThreadLocal是用于主子线程之间参数传递的,因为 I nheritableThreadLocal 的实现原理是, 当创建一个新线程时,子线程会从父线程拷贝一份 InheritableThreadLocal 的值。

60秒标准回答:

InheritableThreadLocal是用于主子线程之间参数传递的,因为 I nheritableThreadLocal 的实现原理是, 当创建一个新线程时,子线程会从父线程拷贝一份 InheritableThreadLocal 的值。这个拷贝发生在线程创建时(即 Thread.init() 阶段)

但是,这种方式有一个问题,那就是必须要是在主线程中手动创建的子线程才可以,而现在池化技术非常普遍了,很多时候线程都是通过线程池进行创建和复用的,这时候InheritableThreadLocal就不行了

因为线程池中的线程是预先创建好并复用的,任务提交时并不会创建新线程。因此,即使主线程设置了 InheritableThreadLocal 的值,在线程池中的工作线程早已创建完毕,不会再次执行“继承拷贝”逻辑。所以, 在线程池中使用 InheritableThreadLocal 无法传递上下文

**答题顺序:**结论 → 原理/机制 → 关键流程 → 场景与取舍 → 易错点

回答主线:

  • **要点1:**但是,这种方式有一个问题,那就是必须要是在主线程中手动创建的子线程才可以,而现在池化技术非常普遍了,很多时候线程都是通过线程池进行创建和复用的,这时候InheritableThreadLocal就不行了。
  • **要点2:**因为线程池中的线程是预先创建好并复用的,任务提交时并不会创建新线程。
  • **要点3:**TransmittableThreadLocal是阿里开源的一个方案 (开源地址: ) ,这个类继承并加强InheritableThreadLocal类。
  • **要点4:**TTL不是靠线程创建时继承,而是通过装饰任务(Runnable/Callable) 的方式,在任务提交到线程池前捕获当前线程的上下文,在任务执行时还原到工作线程,执行完后再清除。
  • **要点5:**使用 TtlRunnable.get(runnable) 或 TtlCallable.get(callable) 包装原始任务;

**记忆锚点:**InheritableThreadLocal → Runnable → Callable → TransmittableThreadLocal → 之间参数传递 → TTL

加分表达:

  • 因此,TTL 在线程池场景下也是有效的。
  • 用来实现线程之间的参数传递,一经常被用在以下场景中: 分布式跟踪系统 或 全链路压测(即链路打标) 日志收集记录系统上下文 Session 级 Cache 应用容器或上层框架跨应用代码给下层SDK传递信息 TTL不是靠线程创建时继承,而是通过装饰任务(Runnable/Callable) 的方式,在任务提交到线程池前捕获当前线程的上下文,在任务执行时还原到工…
  • 先需要导入依赖: 对于简单的父子线程之间参数传递,可以用以下方式: 如果在线程池中,可以用如下方式使用: 除了Runnable,Callable也支持: 也可以直接用在线程池上,而不是Runnable和Callable上:

追问准备:

  • 围绕「InheritableThreadLocal」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「Runnable」:底层原理是什么?使用时有哪些边界和常见坑?
  • 围绕「Callable」:底层原理是什么?使用时有哪些边界和常见坑?
  • 如果线上出现异常,你会如何定位、验证并规避?

典型回答 ​

InheritableThreadLocal是用于主子线程之间参数传递的,因为InheritableThreadLocal的实现原理是,当创建一个新线程时,子线程会从父线程拷贝一份 **InheritableThreadLocal** 的值。这个拷贝发生在线程创建时(即 **Thread.init()** 阶段)。

但是,这种方式有一个问题,那就是必须要是在主线程中手动创建的子线程才可以,而现在池化技术非常普遍了,很多时候线程都是通过线程池进行创建和复用的,这时候InheritableThreadLocal就不行了。

因为线程池中的线程是预先创建好并复用的,任务提交时并不会创建新线程。因此,即使主线程设置了 InheritableThreadLocal 的值,在线程池中的工作线程早已创建完毕,不会再次执行“继承拷贝”逻辑。所以,在线程池中使用 **InheritableThreadLocal** 无法传递上下文。

TransmittableThreadLocal是阿里开源的一个方案 (开源地址:https://github.com/alibaba/transmittable-thread-local ) ,这个类继承并加强InheritableThreadLocal类。用来实现线程之间的参数传递,一经常被用在以下场景中:

  1. 分布式跟踪系统 或 全链路压测(即链路打标)
  2. 日志收集记录系统上下文
  3. Session级Cache
  4. 应用容器或上层框架跨应用代码给下层SDK传递信息

TTL不是靠线程创建时继承,而是通过装饰任务(Runnable/Callable) 的方式,在任务提交到线程池前捕获当前线程的上下文,在任务执行时还原到工作线程,执行完后再清除。

使用 TtlRunnable.get(runnable) 或 TtlCallable.get(callable) 包装原始任务;在 run() 方法开始前,将捕获的上下文 set 到当前线程;执行完后 restore 原有状态。 因此,TTL 在线程池场景下也是有效的。

扩展知识 ​

使用方式 ​

先需要导入依赖:

plain
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>transmittable-thread-local</artifactId>
    <version>2.14.2</version>
</dependency>

对于简单的父子线程之间参数传递,可以用以下方式:

plain
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();

// 在父线程中设置
context.set("value-set-in-parent");

// 在子线程中可以读取,值是"value-set-in-parent"
String value = context.get();

如果在线程池中,可以用如下方式使用:

plain
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();

// 在父线程中设置
context.set("value-set-in-parent");

Runnable task = new RunnableTask();
// 额外的处理,生成修饰了的对象ttlRunnable
Runnable ttlRunnable = TtlRunnable.get(task);
executorService.submit(ttlRunnable);

// Task中可以读取,值是"value-set-in-parent"
String value = context.get();

除了Runnable,Callable也支持:

plain
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();

// 在父线程中设置
context.set("value-set-in-parent");

Callable call = new CallableTask();
// 额外的处理,生成修饰了的对象ttlCallable
Callable ttlCallable = TtlCallable.get(call);
executorService.submit(ttlCallable);

// Call中可以读取,值是"value-set-in-parent"

也可以直接用在线程池上,而不是Runnable和Callable上:

plain
ExecutorService executorService = ...
// 额外的处理,生成修饰了的对象executorService
executorService = TtlExecutors.getTtlExecutorService(executorService);

TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();

// 在父线程中设置
context.set("value-set-in-parent");

Runnable task = new RunnableTask();
Callable call = new CallableTask();
executorService.submit(task);
executorService.submit(call);

// Task或是Call中可以读取,值是"value-set-in-parent"
String value = context.get();