### 使用 `TransmittableThreadLocal` 的步骤及核心原理
#### **一、TransmittableThreadLocal 的作用**
`TransmittableThreadLocal` 是阿里巴巴开源的工具类,用于在多线程环境(尤其是线程池)中**跨线程传递线程本地变量(ThreadLocal)**。它解决了传统 `ThreadLocal` 和 `InheritableThreadLocal` 在线程池中无法正确传递上下文的问题。
---
#### **二、核心使用场景**
1. **线程池任务提交**:确保线程池中的任务能访问提交线程的上下文(如 TraceID、用户信息)。
2. **异步编程**:在异步回调或 CompletableFuture 中传递上下文。
3. **分布式跟踪**:在微服务调用链中保持请求上下文的一致性。
---
#### **三、使用步骤**
##### 1. **添加依赖**
在项目中引入 `transmittable-thread-local` 依赖:
```xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.14.2</version>
</dependency>
```
##### 2. **定义 TransmittableThreadLocal**
创建 `TransmittableThreadLocal` 实例并设置初始值:
```java
private static final TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
```
##### 3. **设置和获取值**
在父线程中设置值:
```java
context.set("request-id-123");
```
在子线程(如线程池任务)中获取值:
```java
String value = context.get(); // 输出 "request-id-123"
```
##### 4. **装饰线程池**
通过 `TtlExecutors` 包装线程池,使任务能自动传递上下文:
```java
ExecutorService executor = Executors.newFixedThreadPool(4);
ExecutorService ttlExecutor = TtlExecutors.getTtlExecutorService(executor);
// 提交任务
ttlExecutor.submit(() -> {
System.out.println("Context in task: " + context.get());
});
```
##### 5. **手动传递上下文(非线程池场景)**
使用 `TtlRunnable` 或 `TtlCallable` 包装任务:
```java
Runnable task = () -> {
System.out.println("Context: " + context.get());
};
Runnable ttlTask = TtlRunnable.get(task);
new Thread(ttlTask).start();
```
---
#### **四、核心原理**
##### 1. **上下文捕获与恢复**
- **捕获阶段**:在任务提交时,通过 `TransmittableThreadLocal.Transmitter.capture()` 捕获当前线程的所有 `TransmittableThreadLocal` 变量值,生成一个快照(`Map<TransmittableThreadLocal<?>, Object>`)。
- **恢复阶段**:在任务执行前,通过 `TransmittableThreadLocal.Transmitter.replay()` 将快照中的值设置到执行线程的 `TransmittableThreadLocal` 中。
- **清理阶段**:任务执行完成后,调用 `TransmittableThreadLocal.Transmitter.restore()` 恢复执行线程原有的上下文,避免污染后续任务。
##### 2. **任务包装机制**
- 通过 `TtlRunnable` 或 `TtlExecutors` 装饰任务,重写 `run()` 方法:
```java
public void run() {
Map<TransmittableThreadLocal<?>, Object> captured = capture();
try {
replay(captured);
delegate.run(); // 执行原始任务
} finally {
restore();
}
}
```
##### 3. **TransmittableThreadLocal 注册**
- 所有 `TransmittableThreadLocal` 实例会通过 `WeakHashMap` 自动注册到全局管理器,确保垃圾回收后自动移除,避免内存泄漏。
##### 4. **线程池兼容性**
- 通过装饰线程池(`TtlExecutors`),确保所有提交的任务都会被包装,从而自动处理上下文传递。
---
#### **五、关键代码实现**
##### 1. **TransmittableThreadLocal 类**
```java
public class TransmittableThreadLocal<T> extends InheritableThreadLocal<T> {
// 全局注册表(WeakHashMap 防止内存泄漏)
private static final Set<TransmittableThreadLocal<?>> holder =
Collections.newSetFromMap(new WeakHashMap<>());
public TransmittableThreadLocal() {
holder.add(this);
}
@Override
public final T get() {
return super.get();
}
@Override
public final void set(T value) {
super.set(value);
}
}
```
##### 2. **Transmitter 类(上下文快照)**
```java
public static class Transmitter {
// 捕获当前线程的上下文
public static Map<TransmittableThreadLocal<?>, Object> capture() {
Map<TransmittableThreadLocal<?>, Object> captured = new HashMap<>();
for (TransmittableThreadLocal<?> ttl : holder) {
captured.put(ttl, ttl.copyValue());
}
return captured;
}
// 恢复上下文到执行线程
public static Map<TransmittableThreadLocal<?>, Object> replay(Map<TransmittableThreadLocal<?>, Object> captured) {
Map<TransmittableThreadLocal<?>, Object> backup = new HashMap<>();
for (TransmittableThreadLocal<?> ttl : holder) {
backup.put(ttl, ttl.get());
ttl.set(captured.get(ttl));
}
return backup;
}
// 恢复执行线程的原始上下文
public static void restore(Map<TransmittableThreadLocal<?>, Object> backup) {
for (TransmittableThreadLocal<?> ttl : holder) {
ttl.set(backup.get(ttl));
}
}
}
```
---
#### **六、注意事项**
1. **避免内存泄漏**:及时调用 `remove()` 清理不再需要的上下文。
2. **线程池装饰**:确保所有任务通过 `TtlExecutors` 提交。
3. **兼容性**:不支持 `ForkJoinPool`,需使用 `TtlForkJoinPoolHelper` 处理。
---
#### **七、总结**
`TransmittableThreadLocal` 通过 **任务装饰** 和 **上下文快照** 机制,在任务提交时捕获当前线程的上下文,并在执行线程中恢复该上下文,最终清理恢复原状态。其核心在于透明地传递线程本地变量,适用于线程池、异步编程等复杂多线程场景。
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传