一、简介
TransmittableThreadLocal(简称 TTL)是阿里巴巴开源的解决线程池上下文传递 问题的工具。它解决了 InheritableThreadLocal 在线程池场景下无法传递上下文的痛点。
核心问题
-
ThreadLocal:无法在父子线程间传递
-
InheritableThreadLocal:可以在创建时传递,但线程复用场景(线程池)无效
适用场景
-
链路追踪(TraceId 传递)
-
用户认证信息传递
-
配置信息传递
-
请求上下文传递
二、快速开始
1. Maven 依赖
html
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.14.4</version>
</dependency>
2. 基本使用
java
import com.alibaba.ttl.TransmittableThreadLocal;
public class TTLBasicDemo {
// 创建 TTL 实例
private static TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
public static void main(String[] args) {
// 设置值
ttl.set("main-value");
// 普通线程能传递
new Thread(() -> {
System.out.println("普通线程: " + ttl.get()); // 输出: main-value
}).start();
// 线程池场景
ExecutorService executor = Executors.newFixedThreadPool(1);
// 未包装的任务无法传递
executor.submit(() -> {
System.out.println("未包装: " + ttl.get()); // 输出: null
});
// 包装后的任务可以传递
executor.submit(TtlRunnable.get(() -> {
System.out.println("TTL包装: " + ttl.get()); // 输出: main-value
}));
executor.shutdown();
}
}
三、核心 API
1. TransmittableThreadLocal
java
// 创建 TTL
TransmittableThreadLocal<String> ttl = new TransmittableThreadLocal<>();
// 基本操作
ttl.set("value"); // 设置值
String value = ttl.get(); // 获取值
ttl.remove(); // 移除值
// 初始化值
TransmittableThreadLocal<String> ttlWithInit = new TransmittableThreadLocal<String>() {
@Override
protected String initialValue() {
return "default-value";
}
};
2. 包装工具类
java
// Runnable 包装
Runnable wrappedRunnable = TtlRunnable.get(originalRunnable);
// Callable 包装
Callable wrappedCallable = TtlCallable.get(originalCallable);
// 线程池包装
ExecutorService executor = TtlExecutors.getTtlExecutorService(originalExecutor);
四、线程池包装方式
方式一:包装任务(推荐用于临时场景)
java
ExecutorService executor = Executors.newFixedThreadPool(5);
// 每次提交时包装
executor.submit(TtlRunnable.get(() -> {
System.out.println(ttl.get());
}));
方式二:包装线程池(推荐用于全局配置)
java
ExecutorService originalExecutor = Executors.newFixedThreadPool(5);
ExecutorService ttlExecutor = TtlExecutors.getTtlExecutorService(originalExecutor);
// 直接提交,自动包装
ttlExecutor.submit(() -> {
System.out.println(ttl.get()); // 自动传递
});
支持的各种线程池
java
// 普通线程池
ExecutorService executor = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(5));
// 定时任务线程池
ScheduledExecutorService scheduledExecutor = TtlExecutors.getTtlScheduledExecutorService(
Executors.newScheduledThreadPool(5)
);
// ForkJoinPool
ForkJoinPool forkJoinPool = TtlExecutors.getTtlForkJoinPool(new ForkJoinPool(5));
五、实战案例
案例1:链路追踪 TraceId 传递
java
public class TraceContext {
private static TransmittableThreadLocal<String> traceIdHolder = new TransmittableThreadLocal<>();
public static void setTraceId(String traceId) {
traceIdHolder.set(traceId);
}
public static String getTraceId() {
return traceIdHolder.get();
}
public static void clear() {
traceIdHolder.remove();
}
}
// 拦截器设置 TraceId
@Component
public class TraceInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String traceId = UUID.randomUUID().toString();
TraceContext.setTraceId(traceId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
TraceContext.clear();
}
}
// 业务代码中使用
@Service
public class OrderService {
@Autowired
private ThreadPoolExecutor threadPool;
public void processOrder() {
String traceId = TraceContext.getTraceId();
// 使用包装后的线程池,自动传递 TraceId
threadPool.submit(() -> {
// 这里能获取到 TraceId
log.info("处理订单,traceId: {}", TraceContext.getTraceId());
});
}
}
案例2:用户上下文传递
java
public class UserContext {
private static TransmittableThreadLocal<UserInfo> userHolder = new TransmittableThreadLocal<>();
public static void setUser(UserInfo user) {
userHolder.set(user);
}
public static UserInfo getUser() {
return userHolder.get();
}
public static Long getUserId() {
UserInfo user = getUser();
return user != null ? user.getId() : null;
}
}
// 使用示例
@Service
public class AsyncService {
private ExecutorService executor = TtlExecutors.getTtlExecutorService(
Executors.newFixedThreadPool(10)
);
public void asyncProcess() {
UserInfo currentUser = UserContext.getUser();
executor.submit(() -> {
// 自动获取当前用户上下文
UserInfo user = UserContext.getUser();
System.out.println("处理用户: " + user.getName());
});
}
}
案例3:Spring Boot 集成
java
@Configuration
public class ThreadPoolConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
// 包装 ThreadPoolTaskExecutor
return TtlExecutors.getTtlThreadPoolTaskExecutor(executor);
}
@Bean
public ExecutorService customExecutor() {
ExecutorService executor = Executors.newFixedThreadPool(10);
return TtlExecutors.getTtlExecutorService(executor);
}
}
六、高级特性
1. 值拷贝机制
java
// 实现 TransmittableThreadLocal.Transmittable 接口自定义拷贝
TransmittableThreadLocal<List<String>> ttl = new TransmittableThreadLocal<List<String>>() {
@Override
public List<String> copy(List<String> parentValue) {
// 深拷贝,避免引用传递导致的数据不一致
return parentValue != null ? new ArrayList<>(parentValue) : null;
}
};
2. 禁用 TTL(全局配置)
java
// 通过系统属性禁用
System.setProperty("ttl.agent.enabled", "false");
// 或通过环境变量
-Dttl.agent.enabled=false
3. 优雅关闭清理
java
@Component
public class TtlCleanupFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} finally {
// 清理所有 TTL 值,防止内存泄漏
TransmittableThreadLocal.Transmitter.clear();
}
}
}
七、性能优化建议
1. 优先包装线程池而非任务
- 包装线程池性能更好,避免每次任务创建额外对象
2. 及时清理
java
try {
ttl.set(value);
// 业务逻辑
} finally {
ttl.remove(); // 重要:避免内存泄漏
}
3. 使用静态变量
java
// 推荐:定义为静态变量
private static final TransmittableThreadLocal<String> CONTEXT = new TransmittableThreadLocal<>();
// 不推荐:每次使用时创建
public void method() {
TransmittableThreadLocal<String> local = new TransmittableThreadLocal<>();
}
八、常见问题与解决方案
1. 值未传递问题
原因:未正确包装线程池或任务
解决:
java
// 错误
executor.submit(() -> {});
// 正确
executor.submit(TtlRunnable.get(() -> {}));
// 或使用包装后的线程池
2. 内存泄漏
原因 :未调用 remove() 清理
解决:
java
try {
ttl.set(value);
// 业务逻辑
} finally {
ttl.remove(); // 确保清理
}
3. 嵌套线程池问题
java
// 外层线程池
ExecutorService outer = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(5));
outer.submit(() -> {
// 内层线程池也需要包装
ExecutorService inner = TtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(5));
inner.submit(() -> {
// 可以正常传递
});
});
九、对比总结
| 特性 | ThreadLocal | InheritableThreadLocal | TransmittableThreadLocal |
|---|---|---|---|
| 父子线程传递 | ❌ | ✅ | ✅ |
| 线程池传递 | ❌ | ❌ | ✅ |
| 性能开销 | 低 | 中 | 中 |
| 内存泄漏风险 | 中 | 高 | 低(需手动清理) |
| 适用场景 | 线程内隔离 | 简单父子线程 | 线程池/异步场景 |
十、最佳实践总结
-
优先包装线程池 :使用
TtlExecutors包装线程池,避免遗漏任务包装 -
及时清理 :在 finally 块中调用
remove()方法 -
静态定义:将 TTL 定义为静态常量,避免重复创建
-
深拷贝 :对于可变对象,实现
copy()方法进行深拷贝 -
统一管理:使用拦截器/过滤器统一管理上下文的设置和清理
-
监控告警:监控 TTL 的内存占用,设置合理告警