ThreadLocal / InheritableThreadLocal / TransmittableThreadLocal(TTL)学习总结

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();
}

工程铁律(必须遵守)

  1. ThreadLocal 用完必须 remove
  2. 在线程池 / Web / RPC / MQ 环境中尤其重要
  3. 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 继承
线程池传递
自动清理
生产推荐

七、工程级"无脑规则"

  1. ThreadLocal:用完必 remove
  2. 业务代码避免 InheritableThreadLocal
  3. 跨线程池传上下文:只用 TTL 或框架方案

八、典型使用场景

  • 日志 MDC(traceId)
  • 用户 / 租户上下文
  • RPC 调用链路信息
  • 请求级缓存

九、一句话总复盘

  • ThreadLocal:线程内隔离
  • InheritableThreadLocal:仅适合 new Thread
  • TTL:线程池场景的正确解法

只要涉及线程池 + 上下文传递,TTL 是唯一安全选择

相关推荐
鹿角片ljp3 小时前
LeetCode 64:最小路径和复盘|二维 DP 与 ACM 模式完整写法
java·数据结构·算法
IPdodo_3 小时前
跨境 API 调用不稳定怎么办:出口、超时重试与链路监控的实践
网络·python·网络协议
aramae3 小时前
模拟实现strcmp()(C语言)
c语言·开发语言·后端
泡海椒4 小时前
PDF 表格样式优化:jquick-pdf 边框、圆角、背景色
java·开发语言·pdf
Beyond_System|系统之外5 小时前
【学编程】Python基础编程题100道(21-60)
开发语言·python·算法
景熙55235 小时前
15.Java 8 Stream 流入门到实战
java·开发语言·数据结构
wno7046 小时前
Spring Security权限控制
java·python·spring
xiaoye-duck6 小时前
《Linux 网络编程》深入理解 IP 协议(二):网段划分、私有 IP 与 NAT 地址转换
linux·网络·ip
杨运交6 小时前
[071][验证码模块]基于Spring拦截器的验证码认证设计思想
java·后端·spring