ThreadLocal / InheritableThreadLocal / TransmittableThreadLocal(TTL)学习总结
一、ThreadLocal 是什么
一句话定义:
ThreadLocal 是一种 线程私有变量机制,用于在同一线程内共享数据,而不同线程之间数据完全隔离。
核心特性
- 同一个 ThreadLocal,在不同线程中有不同的值
- 不依赖锁实现线程安全
- 本质不是"共享",而是"隔离"
数据存储位置(关键)
Thread
└─ ThreadLocalMap
├─ key: ThreadLocal(弱引用)
└─ value: 实际数据(强引用)
ThreadLocal 本身不存数据,数据存在线程内部
二、ThreadLocal 的正确使用方式
基本用法
java
ThreadLocal<User> tl = new ThreadLocal<>();
try {
tl.set(user);
// 业务逻辑
} finally {
tl.remove();
}
工程铁律(必须遵守)
- ThreadLocal 用完必须 remove
- 在线程池 / Web / RPC / MQ 环境中尤其重要
- value 不应是大对象或长期对象
为什么会内存泄漏
- 线程池中的线程不会结束
- ThreadLocalMap 的 key 是弱引用,会被 GC
- value 是强引用,会一直挂在线程上
泄漏的不是 ThreadLocal,而是 value
三、InheritableThreadLocal 是什么
定义
InheritableThreadLocal 是 ThreadLocal 的一个变体,允许 子线程继承父线程中的值。
继承发生的时机(非常重要)
- 仅在
new Thread()时发生 - 不是 set 时
- 不是 get 时
- 与线程池无关
示例
java
InheritableThreadLocal<String> itl = new InheritableThreadLocal<>();
itl.set("A");
new Thread(() -> {
System.out.println(itl.get()); // 输出 A
}).start();
为什么它很危险
-
线程池中的线程不是新建线程
-
不会触发继承逻辑
-
极易造成:
- 上下文串请求
- 串用户
- 串 traceId
工程结论
业务代码中几乎不应该使用 InheritableThreadLocal
四、TransmittableThreadLocal(TTL)是什么
背景问题
- ThreadLocal:线程池中无法传递上下文
- InheritableThreadLocal:线程池中行为错误
TTL 的一句话定义
TTL 在提交任务时捕获当前线程的 ThreadLocal 上下文,并在任务执行前后自动传递和恢复
关键特性
- 对线程池友好
- 提交任务时传递上下文
- 执行结束后自动清理
五、TTL 的标准使用方式(生产级)
1️⃣ 定义 TTL
java
TransmittableThreadLocal<String> TRACE_ID =
new TransmittableThreadLocal<>();
2️⃣ 设置和清理(请求入口)
java
try {
TRACE_ID.set(traceId);
// 同步 + 异步逻辑
} finally {
TRACE_ID.remove();
}
3️⃣ 包装线程池(关键步骤)
java
ExecutorService executor =
TtlExecutors.getTtlExecutorService(
Executors.newFixedThreadPool(8)
);
不包装线程池,TTL 不生效
4️⃣ 提交任务
java
executor.submit(() -> {
log.info("traceId={}", TRACE_ID.get());
});
六、三者对比总结
| 特性 | ThreadLocal | InheritableThreadLocal | TTL |
|---|---|---|---|
| 线程隔离 | ✅ | ✅ | ✅ |
| new Thread 继承 | ❌ | ✅ | ✅ |
| 线程池传递 | ❌ | ❌ | ✅ |
| 自动清理 | ❌ | ❌ | ✅ |
| 生产推荐 | ✅ | ❌ | ✅ |
七、工程级"无脑规则"
- ThreadLocal:用完必 remove
- 业务代码避免 InheritableThreadLocal
- 跨线程池传上下文:只用 TTL 或框架方案
八、典型使用场景
- 日志 MDC(traceId)
- 用户 / 租户上下文
- RPC 调用链路信息
- 请求级缓存
九、一句话总复盘
- ThreadLocal:线程内隔离
- InheritableThreadLocal:仅适合 new Thread
- TTL:线程池场景的正确解法
只要涉及线程池 + 上下文传递,TTL 是唯一安全选择