Java中使用阿里巴巴開源庫TransmittableThreadLocal:跨線程上下文傳遞講解
Java中使用阿里巴巴開源庫TransmittableThreadLocal跨線程上下文傳遞講解問題背景Java 的ThreadLocal只能在當前線程內讀寫數據。當任務被提交到線程池執(zhí)行時子線程線程池中的工作線程無法讀取到父線程設置的 ThreadLocal 值。ThreadLocalStringcontextnewThreadLocal();context.set(user-007);executorService.submit(()-{System.out.println(context.get());// 輸出 null});JDK 提供了InheritableThreadLocal可以在new Thread()創(chuàng)建子線程時復制父線程的值。但線程池中的線程是復用的不是每次都新建的所以InheritableThreadLocal在線程池場景下也失效。注博客https://blog.csdn.net/badao_liumang_qizhiTransmittableThreadLocal 是什么TransmittableThreadLocalTTL是阿里巴巴開源的一個庫解決的核心問題是在線程池復用線程時仍然能正確傳遞 ThreadLocal 值。原理簡述在任務提交到線程池時捕獲當前線程的 TTL 值快照在任務執(zhí)行前回放快照到工作線程任務執(zhí)行完畢后恢復工作線程原來的值。提交線程父 線程池工作線程子 ┌──────────────┐ ┌──────────────┐ │ TTL 007 │ │ TTL null │ │ │ │ │ │ submit(task) ├───┐ │ │ └──────────────┘ │ └──────────────┘ │ │ ① 提交時捕獲快照 {007} │ ② 執(zhí)行前回放 → TTL 007 │ ③ 執(zhí)行中task 讀到 007 │ ④ 執(zhí)行后恢復 → TTL nullMaven 依賴dependencygroupIdcom.alibaba/groupIdartifactIdtransmittable-thread-local/artifactIdversion2.14.3/version/dependency基礎用法1. 聲明 TransmittableThreadLocalimportcom.alibaba.ttl.TransmittableThreadLocal;publicclassRequestContext{privatestaticfinalTransmittableThreadLocalStringUSER_IDnewTransmittableThreadLocal();privatestaticfinalTransmittableThreadLocalStringTRACE_IDnewTransmittableThreadLocal();publicstaticvoidsetUserId(StringuserId){USER_ID.set(userId);}publicstaticStringgetUserId(){returnUSER_ID.get();}publicstaticvoidsetTraceId(StringtraceId){TRACE_ID.set(traceId);}publicstaticStringgetTraceId(){returnTRACE_ID.get();}publicstaticvoidclear(){USER_ID.remove();TRACE_ID.remove();}}2. 包裝線程池TTL 需要對線程池進行包裝才能生效有三種方式importcom.alibaba.ttl.threadpool.TtlExecutors;importjava.util.concurrent.ExecutorService;importjava.util.concurrent.Executors;// 方式一包裝已有線程池推薦最常用ExecutorServicerawPoolExecutors.newFixedThreadPool(4);ExecutorServicettlPoolTtlExecutors.getTtlExecutorService(rawPool);// 方式二包裝 ScheduledExecutorServiceScheduledExecutorServicerawScheduledExecutors.newScheduledThreadPool(2);ScheduledExecutorServicettlScheduledTtlExecutors.getTtlScheduledExecutorService(rawScheduled);3. 使用示例publicclassDemo{privatestaticfinalExecutorServicepoolTtlExecutors.getTtlExecutorService(Executors.newFixedThreadPool(4));publicstaticvoidmain(String[]args)throwsException{// 主線程設置上下文RequestContext.setUserId(007);RequestContext.setTraceId(trace-abc-123);// 提交到線程池子線程能讀到pool.submit(()-{System.out.println(RequestContext.getUserId());// 007System.out.println(RequestContext.getTraceId());// trace-abc-123}).get();// 修改后再提交子線程讀到新值RequestContext.setUserId(008);pool.submit(()-{System.out.println(RequestContext.getUserId());// 008}).get();RequestContext.clear();}}三種傳遞方式對比方式使用場景說明包裝線程池推薦首選TtlExecutors.getTtlExecutorService(pool)包裝 Runnable/Callable無法修改線程池時TtlRunnable.get(runnable)Java Agent無侵入全局生效JVM 啟動參數-javaagent:transmittable-thread-local-x.x.x.jar包裝 Runnable不改線程池importcom.alibaba.ttl.TtlRunnable;ExecutorServicerawPoolExecutors.newFixedThreadPool(4);RequestContext.setUserId(007);// 包裝任務而非線程池Runnabletask()-System.out.println(RequestContext.getUserId());RunnablettlTaskTtlRunnable.get(task);rawPool.submit(ttlTask);// 輸出 007Java Agent 方式零侵入JVM 啟動參數加上-javaagent:/path/to/transmittable-thread-local-2.14.3.jar無需包裝線程池或 Runnable所有 ThreadLocal 自動具備跨線程傳遞能力。適合無法修改代碼的場景或全局啟用。Spring Boot 集成配置異步線程池importcom.alibaba.ttl.threadpool.TtlExecutors;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;importjava.util.concurrent.Executor;ConfigurationpublicclassThreadPoolConfig{Bean(asyncExecutor)publicExecutorasyncExecutor(){ThreadPoolTaskExecutorexecutornewThreadPoolTaskExecutor();executor.setCorePoolSize(8);executor.setMaxPoolSize(16);executor.setQueueCapacity(200);executor.setThreadNamePrefix(async-);executor.initialize();// 關鍵用 TTL 包裝returnTtlExecutors.getTtlExecutorService(executor.getThreadPoolExecutor());}}攔截器設置上下文importjakarta.servlet.http.HttpServletRequest;importjakarta.servlet.http.HttpServletResponse;importorg.springframework.stereotype.Component;importorg.springframework.web.servlet.HandlerInterceptor;ComponentpublicclassContextInterceptorimplementsHandlerInterceptor{OverridepublicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler){RequestContext.setUserId(request.getHeader(X-User-Id));RequestContext.setTraceId(request.getHeader(X-Trace-Id));returntrue;}OverridepublicvoidafterCompletion(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler,Exceptionex){RequestContext.clear();// 必須清理防止內存泄漏}}業(yè)務使用ServicepublicclassOrderService{AutowiredQualifier(asyncExecutor)privateExecutorasyncExecutor;publicvoidcreateOrder(OrderDTOdto){// 當前HTTP線程設置的上下文// RequestContext.getUserId() 007// 異步處理子線程也能讀到 007CompletableFuture.runAsync(()-{StringuserIdRequestContext.getUserId();// 007 ?auditService.record(userId,創(chuàng)建訂單);},asyncExecutor);}}與 InheritableThreadLocal 的區(qū)別// 測試代碼InheritableThreadLocalStringitlnewInheritableThreadLocal();TransmittableThreadLocalStringttlnewTransmittableThreadLocal();ExecutorServicepoolExecutors.newFixedThreadPool(1);// 只有1個線程必定復用// 第一次提交線程首次創(chuàng)建ITL 和 TTL 都能傳遞itl.set(A);ttl.set(A);pool.submit(()-{System.out.println(ITLitl.get());// ASystem.out.println(TTLttl.get());// A}).get();// 第二次提交線程被復用ITL 還是舊值TTL 是新值itl.set(B);ttl.set(B);pool.submit(()-{System.out.println(ITLitl.get());// A舊值線程復用不會重新繼承System.out.println(TTLttl.get());// B正確每次提交重新捕獲}).get();特性ThreadLocalInheritableThreadLocalTransmittableThreadLocal當前線程讀寫???new Thread 繼承???線程池復用傳遞???值更新后子線程感知-??適用場景場景說明鏈路追蹤traceId、spanId 跨線程透傳用戶身份傳遞HTTP 攔截器設置 userId異步任務中使用日志 MDC跨線程保持日志上下文字段一致多租戶隔離tenantId 在線程池中不丟失灰度/AB測試流量標記在異步鏈路中傳遞注意事項必須清理HTTP 請求結束后調remove()防止線程池復用時數據泄漏到下一個請求不能跨 JVMTTL 只解決進程內跨線程問題跨 MQ、RPC 調用需要通過消息 Header 或 RPC Context 手動傳遞包裝要完整項目中所有線程池都需要包裝遺漏任何一個都會導致該路徑上下文丟失避免存大對象TTL 每次提交任務都會做快照拷貝淺拷貝存大對象有性能和一致性風險配合 RPC 框架Dubbo、gRPC 等通常有自己的 Context 傳遞機制TTL 負責進程內RPC Context 負責進程間TTL 不能解決的問題進程A (線程池內傳遞 ?) 進程B (需要額外手段) ┌─────────────────────┐ ┌─────────────────────┐ │ HTTP線程 → 線程池 │── MQ/RPC ──→│ 消費線程 │ │ TTL 有效 ? │ │ TTL 管不到 ? │ └─────────────────────┘ └─────────────────────┘ 需要通過 MQ Header / RPC Attachment 傳遞跨進程傳遞需要配合其他手段MQ UserProperty、RPC Attachment、HTTP Header 等TTL 只負責進程內線程間的透傳。

相關新聞

低壓工況下閥門密封性能的影響因素與試驗評價方法

低壓工況下閥門密封性能的影響因素與試驗評價方法

1. 問題背景在閥門壓力試驗中,常見的一種認識是:如果閥門能夠通過高壓密封試驗,那么低壓密封性能通常也不會存在問題。這一認識并不適用于所有閥門結構。閥門的密封性能取決于密封副接觸壓力、密封面形貌、關閉件定位、閥座預緊以及介質壓差等…

2026/7/29 8:16:09 閱讀更多
AI如何提升學術寫作效率與質量

AI如何提升學術寫作效率與質量

1. 當AI遇上學術寫作:一場生產力革命的開端 十年前我完成第一部學術專著時,整整耗費了兩年半的周末和假期。如今看著團隊里的年輕學者用AI工具三個月就能完成同等質量的初稿,這種代際差異讓我深刻意識到:學術寫作正在經歷古騰堡印…

2026/7/29 9:06:11 閱讀更多
iOS持續(xù)集成中證書管理的最佳實踐與解決方案

iOS持續(xù)集成中證書管理的最佳實踐與解決方案

1. iOS持續(xù)集成中的證書管理痛點在iOS應用的CI/CD流程中,證書和配置文件的管理一直是開發(fā)者最頭疼的問題之一。不同于Android開發(fā)可以直接使用調試密鑰,蘋果的生態(tài)要求每個應用都必須使用有效的證書簽名才能安裝到設備上。這就導致在Jenkins自動化構建時…

2026/7/29 9:06:11 閱讀更多
LARA-R6401與PIC18LF45K42的低功耗物聯網設計實踐

LARA-R6401與PIC18LF45K42的低功耗物聯網設計實踐

1. LARA-R6401與PIC18LF45K42的硬件協(xié)同設計在物聯網邊緣計算領域,LARA-R6401 LTE Cat 1模塊與PIC18LF45K42微控制器的組合正在開創(chuàng)低功耗廣域連接的新范式。這個組合最吸引人的特點是:LARA-R6401提供了僅24x26mm的緊湊封裝中集成了全球多頻段LTE連接能力…

2026/7/29 8:56:11 閱讀更多