Java中使用阿里巴巴开源库TransmittableThreadLocal:跨线程上下文传递讲解
Java中使用阿里巴巴开源库TransmittableThreadLocal跨线程上下文传递讲解问题背景Java 的ThreadLocal只能在当前线程内读写数据。当任务被提交到线程池执行时子线程线程池中的工作线程无法读取到父线程设置的 ThreadLocal 值。ThreadLocalStringcontextnewThreadLocal();context.set(user-007);executorService.submit(()-{System.out.println(context.get());// 输出 null});JDK 提供了InheritableThreadLocal可以在new Thread()创建子线程时复制父线程的值。但线程池中的线程是复用的不是每次都新建的所以InheritableThreadLocal在线程池场景下也失效。注博客https://blog.csdn.net/badao_liumang_qizhiTransmittableThreadLocal 是什么TransmittableThreadLocalTTL是阿里巴巴开源的一个库解决的核心问题是在线程池复用线程时仍然能正确传递 ThreadLocal 值。原理简述在任务提交到线程池时捕获当前线程的 TTL 值快照在任务执行前回放快照到工作线程任务执行完毕后恢复工作线程原来的值。提交线程父 线程池工作线程子 ┌──────────────┐ ┌──────────────┐ │ TTL 007 │ │ TTL null │ │ │ │ │ │ submit(task) ├───┐ │ │ └──────────────┘ │ └──────────────┘ │ │ ① 提交时捕获快照 {007} │ ② 执行前回放 → TTL 007 │ ③ 执行中task 读到 007 │ ④ 执行后恢复 → 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();// 必须清理防止内存泄漏}}业务使用ServicepublicclassOrderService{AutowiredQualifier(asyncExecutor)privateExecutorasyncExecutor;publicvoidcreateOrder(OrderDTOdto){// 当前HTTP线程设置的上下文// RequestContext.getUserId() 007// 异步处理子线程也能读到 007CompletableFuture.runAsync(()-{StringuserIdRequestContext.getUserId();// 007 ✓auditService.record(userId,创建订单);},asyncExecutor);}}与 InheritableThreadLocal 的区别// 测试代码InheritableThreadLocalStringitlnewInheritableThreadLocal();TransmittableThreadLocalStringttlnewTransmittableThreadLocal();ExecutorServicepoolExecutors.newFixedThreadPool(1);// 只有1个线程必定复用// 第一次提交线程首次创建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:15:56 阅读更多 →
PLC顺序控制实战:定时器与比较指令实现电机顺序启停

PLC顺序控制实战:定时器与比较指令实现电机顺序启停

1. 项目缘起:一个经典但易错的工业控制场景在工厂的自动化产线上,我们经常会遇到这样的需求:几台电动机需要按照特定的顺序启动,比如先启动主传动电机,再启动送料电机,最后启动冷却风机;而在停机…

2026/7/29 8:15:56 阅读更多 →
C++程序员视角:彻底清除Windows顽固广告弹窗的系统级攻防指南

C++程序员视角:彻底清除Windows顽固广告弹窗的系统级攻防指南

1. 项目概述:从“弹窗”到“系统级”的攻防思维 “怎么彻底关闭广告弹窗?”——这几乎是每个Windows用户都曾咬牙切齿问过的问题。尤其是当你发现,即便卸载了某个软件,它的“屏保广告”或者“清理助手”依然像幽灵一样定时弹出&am…

2026/7/29 8:14:55 阅读更多 →

最新新闻

5万+Star!最强Agent教程来了,吴恩达手把手教你搭建Agent

5万+Star!最强Agent教程来了,吴恩达手把手教你搭建Agent

今天这篇把完整系统化 AI Agent 学习路线一次性讲透,从初识智能体、大模型底层、主流框架、MCP 通信协议、Agentic-RL 训练,到 3 套完整商业实战项目全部梳理清楚,搭配吴恩达经典学习思路,零基础也能循序渐进吃透智能体。文末整理…

2026/7/29 8:26:58 阅读更多 →
Java 8 Stream API实战:List转Map的多种方式与最佳实践

Java 8 Stream API实战:List转Map的多种方式与最佳实践

1. 项目概述&#xff1a;从集合操作到Map转换的实战需求在日常的Java开发中&#xff0c;尤其是处理数据集合时&#xff0c;我们经常遇到一个非常具体的场景&#xff1a;手里有一个List<T>对象&#xff0c;需要根据列表中对象的某个属性&#xff08;或通过某种规则计算出的…

2026/7/29 8:26:58 阅读更多 →
Android与Arduino蓝牙小车:从硬件连接到App开发全流程实践

Android与Arduino蓝牙小车:从硬件连接到App开发全流程实践

1. 项目缘起&#xff1a;为什么选择Android与Arduino的组合&#xff1f; 几年前&#xff0c;当我第一次尝试用手机控制一个自己组装的四轮小车时&#xff0c;那种感觉非常奇妙。你不再需要依赖一个笨重的遥控器&#xff0c;或者非得在电脑前敲代码&#xff0c;只需要在口袋里掏…

2026/7/29 8:26:58 阅读更多 →
PyTorch安装全攻略:解决下载慢、版本冲突与CUDA配置难题

PyTorch安装全攻略:解决下载慢、版本冲突与CUDA配置难题

1. 项目概述&#xff1a;为什么PyTorch下载总让人头疼&#xff1f; 搞深度学习的朋友&#xff0c;尤其是刚入门的新手&#xff0c;十个里有八个都卡在PyTorch的安装和下载这一步。这几乎成了一个“新手村”的必经考验。你兴致勃勃地打开官网&#xff0c;找到那条看似简单的安装…

2026/7/29 8:26:58 阅读更多 →
MATLAB多子图布局进阶:从subplot到nexttile的高效绘图实践

MATLAB多子图布局进阶:从subplot到nexttile的高效绘图实践

1. 项目概述&#xff1a;从subplot到nexttile的绘图革命如果你用MATLAB画过图&#xff0c;尤其是需要把多个子图&#xff08;subplot&#xff09;排列在一起做对比展示&#xff0c;那你一定对subplot这个函数又爱又恨。爱的是它确实能把图画出来&#xff0c;恨的是每次调整子图…

2026/7/29 8:26:58 阅读更多 →
以太网PHY芯片是做什么的?如何给非技术人员3分钟讲清楚

以太网PHY芯片是做什么的?如何给非技术人员3分钟讲清楚

https://zhuanlan.zhihu.com/p/2054917965926102123?share_codeIH5ckHH2wJQa&utm_psn2065469144603029847 网口不通&#xff1f;先搞懂MAC与PHY给非技术人员讲PHY芯片&#xff0c;它是数码世界的“翻译官”“司机” 1. 核心任务 手机/电脑处理器&#xff08;CPU&#xff0…

2026/7/29 8:25:58 阅读更多 →

日新闻

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

【RT-DETR多模态创新改进】CVPR 2025 | 独家特征融合创新改进篇 | 引入RLAB残差线性注意力模块,有效融合并强调多尺度特征,多种改进点,适合红外与可见光融合目标检测任务,有效涨点

一、本文介绍 🔥本文在RT-DETR多模态融合目标检测中引入RLAB残差线性注意力模块,可在不同模态特征交互阶段进行多次残差细化,使可见光、红外等特征在尺度、语义和空间位置上更好对齐;随后将细化特征与解码器输出拼接并生成Q、K、V,通过线性注意力自适应强化关键通道、目…

2026/7/29 0:00:23 阅读更多 →
AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础

AI编程系列02:合并知识功能,给 AI 问数和 RAG 场景打基础

AI编程系列02&#xff1a;合并知识功能&#xff0c;给 AI 问数和 RAG 场景打基础 在上一期「AI编程系列」中&#xff0c;我们学习了如何构建一个基础的 AI 问答系统&#xff0c;通过简单的输入输出让模型回应问题。但现实世界中的 AI 应用往往需要处理更复杂的场景&#xff1a;…

2026/7/29 0:00:23 阅读更多 →
AI智能体开发实战:从工具调用到企业级部署

AI智能体开发实战:从工具调用到企业级部署

1. 从被动问答到主动执行&#xff1a;AI Agent的范式转变过去两年&#xff0c;大语言模型最显著的应用形态是聊天机器人——用户提问&#xff0c;AI回答。但真正的生产力革命发生在2023年下半年&#xff1a;当AI学会主动调用工具完成任务时&#xff0c;生产力工具的历史被彻底改…

2026/7/29 0:00:23 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档&#xff0c;可以直接使用&#xff01;系统支持图片、视频、摄像头等多种方式检测裂缝&#xff0c;功能强大实用。 1数据集6000张 8各类别

2026/7/28 12:04:22 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像&#xff01; pubg绝地求生目标检测数据集 1分类&#xff1a;e_body&#xff0c;14905个标签&#xff0c;txt格式 共计14244张图&#xff0c;99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/28 8:29:16 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别&#xff1a; allies enemy tag图片总量&#xff1a;7247张训练集&#xff1a;5139张验证集&#xff1a;1425张测试集&#xff1a;683张标注状态&#xff1a;全部已标注&#xff0c;即拿即用数据格式&#xff1a;支持YOLO格式及其他格式&#…

2026/7/28 5:03:42 阅读更多 →

月新闻