在介绍TransmittableThreadLocal之前,我们先回顾一下Java中的ThreadLocal和InheritableThreadLocal。
ThreadLocal提供了线程局部变量,每个线程都可以通过get/set访问自己独立的变量副本。
|
1 2 3 4 5 6 |
ThreadLocal<String> threadLocal = new ThreadLocal<>(); threadLocal.set("main thread value"); new Thread(() -> { System.out.println(threadLocal.get()); // 输出null }).start();<img class="medium-zoom-image" title="点击并拖拽以移动" alt="" /> |
InheritableThreadLocal可以解决父子线程间值传递的问题:`java
|
1 2 3 4 5 6 |
InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>(); inheritableThreadLocal.set("main thread value"); new Thread(() -> { System.out.println(inheritableThreadLocal.get()); // 输出"main thread value" }).start(); |
但是InheritableThreadLocal有局限性:
TransmittableThreadLocal(TTL)是阿里开源的一个线程间数据传递解决方案,解决了InheritableThreadLocal在线程池场景下的问题。
核心特性
TransmittableThreadLocal:继承自InheritableThreadLocalTtlRunnable/TtlCallable:装饰器模式包装Runnable和CallableTransmitter:提供capture/replay/restore机制TTL的核心思想是"捕获-传递-恢复":
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// 伪代码展示TTL工作原理 public class TtlRunnable implements Runnable { private final Runnable runnable; private final Object captured; public TtlRunnable(Runnable runnable) { this.runnable = runnable; this.captured = TransmittableThreadLocal.Transmitter.capture(); } public void run() { Object backup = TransmittableThreadLocal.Transmitter.replay(captured); try { runnable.run(); } finally { TransmittableThreadLocal.Transmitter.restore(backup); } } }<img class="medium-zoom-image" title="点击并拖拽以移动" alt="" /> |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 1. 创建TransmittableThreadLocal变量 TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); // 2. 设置值 context.set("value-set-in-parent"); // 3. 包装Runnable/Callable Runnable task = () -> { System.out.println("获取TTL值: " + context.get()); }; Runnable ttlTask = TtlRunnable.get(task); // 4. 提交到线程池 ExecutorService executor = Executors.newCachedThreadPool(); executor.submit(ttlTask); executor.shutdown(); |
更优雅的方式是使用TtlExecutors包装线程池:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
ExecutorService executorService = Executors.newCachedThreadPool(); // 包装线程池 ExecutorService ttlExecutorService = TtlExecutors.getTtlExecutorService(executorService); TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>(); context.set("value-set-in-parent"); ttlExecutorService.execute(() -> { // 可以获取到父线程设置的上下文 System.out.println(context.get()); }); |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
// 初始化TTL上下文 TransmittableThreadLocal<String> requestId = new TransmittableThreadLocal<>(); TransmittableThreadLocal<User> userInfo = new TransmittableThreadLocal<>(); // 设置值 requestId.set("REQ-123456"); userInfo.set(new User("张三", "admin")); // 异步处理 CompletableFuture.runAsync( () -> { System.out.println("异步任务中获取requestId: " + requestId.get()); System.out.println("异步任务中获取userInfo: " + userInfo.get()); }, TtlExecutors.getTtlExecutorService(ForkJoinPool.commonPool()) ).join(); |
|
1 2 3 |
// 推荐使用withInitial初始化 private static final TransmittableThreadLocal<SimpleDateFormat> DATE_FORMATTER = TransmittableThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); |
|
1 2 3 4 5 |
try { // 使用TTL } finally { ttlVariable.remove(); }<img class="medium-zoom-image" title="点击并拖拽以移动" alt="" /> |
|
1 2 3 4 5 6 7 8 9 |
// 创建线程池 ExecutorService executor = Executors.newFixedThreadPool(5); // 包装线程池 ExecutorService ttlExecutor = TtlExecutors.getTtlExecutorService(executor); // 使用包装后的线程池 ttlExecutor.execute(() -> { // 可以获取TTL值 }); |
问题表现:线程池中的线程长期存活,TTL变量一直存在
解决方案:
|
1 2 3 4 5 |
try { // 业务代码 } finally { ttlVariable.remove(); } |
问题表现:直接使用线程池提交任务,TTL值丢失
错误示例:
|
1 |
executor.execute(task); // 直接提交,TTL失效 |
正确做法:
|
1 2 3 |
executor.execute(TtlRunnable.get(task)); // 包装后提交 // 或 ttlExecutor.execute(task); |
问题表现:Spring的@Async、Hystrix等框架中TTL失效
解决方案:
|
1 2 3 4 5 6 |
@Bean public Executor asyncExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); // 配置executor return TtlExecutors.getTtlExecutorService(executor.getThreadPoolExecutor()); } |
问题表现:多个任务共享线程时,TTL值被覆盖
解决方案:
|
1 2 3 4 5 6 7 8 |
// 设置traceId TransmittableThreadLocal<String> traceId = new TransmittableThreadLocal<>(); void processRequest(Request request) { traceId.set(request.getTraceId()); // 异步处理不影响traceId传递 asyncService.process(request); } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class UserContextHolder { private static final TransmittableThreadLocal<User> CURRENT_USER = new TransmittableThreadLocal<>(); public static void set(User user) { CURRENT_USER.set(user); } public static User get() { return CURRENT_USER.get(); } public static void clear() { CURRENT_USER.remove(); } } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// 租户上下文 public class TenantContext { private static final TransmittableThreadLocal<String> TENANT_ID = new TransmittableThreadLocal<>(); public static void setTenantId(String tenantId) { TENANT_ID.set(tenantId); } public static String getTenantId() { return TENANT_ID.get(); } } // 业务代码中无需显式传递tenantId public void businessMethod() { String tenantId = TenantContext.getTenantId(); // 使用tenantId } |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// 日志上下文 public class LogContext { private static final TransmittableThreadLocal<Map<String, String>> LOG_CONTEXT = TransmittableThreadLocal.withInitial(HashMap::new); public static void put(String key, String value) { LOG_CONTEXT.get().put(key, value); } public static Map<String, String> getContext() { return new HashMap<>(LOG_CONTEXT.get()); } } // 日志切面 @Aspect @Component public class LogAspect { @Around("execution(* com.example..*.*(..))") public Object around(ProceedingJoinPoint pjp) throws Throwable { MDC.setContextMap(LogContext.getContext()); try { return pjp.proceed(); } finally { MDC.clear(); } } } |
| 特性 | ThreadLocal | InheritableThreadLocal | TransmittableThreadLocal |
|---|---|---|---|
| 线程隔离 | 支持 | 支持 | 支持 |
| 父子线程传递 | 不支持 | 支持 | 支持 |
| 线程池支持 | 不支持 | 不支持 | 支持 |
| 执行前后自定义逻辑 | 不支持 | 不支持 | 支持 |
| 性能开销 | 低 | 中 | 中高 |
TransmittableThreadLocal是解决线程池环境下上下文传递的强大工具,合理使用可以简化编程模型,但需要注意内存管理和性能影响。关键点: