深入理解TransmittableThreadLocal:原理、使用与避坑指南

 一、ThreadLocal与InheritableThreadLocal回顾

在介绍TransmittableThreadLocal之前,我们先回顾一下Java中的ThreadLocal和InheritableThreadLocal。

1. ThreadLocal

ThreadLocal提供了线程局部变量,每个线程都可以通过get/set访问自己独立的变量副本。

ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("main thread value");new Thread(() -> {System.out.println(threadLocal.get()); // 输出null
}).start();
2. InheritableThreadLocal

InheritableThreadLocal可以解决父子线程间值传递的问题:```java

InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
inheritableThreadLocal.set("main thread value");new Thread(() -> {System.out.println(inheritableThreadLocal.get()); // 输出"main thread value"
}).start();

但是InheritableThreadLocal有局限性:
- 只支持创建新线程时的值传递
- 线程池场景下不适用(线程复用)

 二、TransmittableThreadLocal介绍

TransmittableThreadLocal(TTL)是阿里开源的一个线程间数据传递解决方案,解决了InheritableThreadLocal在线程池场景下的问题。

核心特性
- 支持线程池场景下的值传递
- 支持任务执行前的自定义逻辑
- 支持任务执行后的自定义逻辑
- 兼容InheritableThreadLocal

三、TransmittableThreadLocal原理
1. 核心类结构

- `TransmittableThreadLocal`:继承自InheritableThreadLocal
- `TtlRunnable`/`TtlCallable`:装饰器模式包装Runnable和Callable
- `Transmitter`:提供capture/replay/restore机制

2. 工作原理

TTL的核心思想是"捕获-传递-恢复":

1. 捕获(Capture):在任务提交到线程池时,捕获当前线程的所有TTL变量
2. 传递(Transmit):将捕获的值传递给线程池中的线程
3. 恢复(Replay):线程池中的线程在执行任务前,将TTL值恢复
4. 回滚(Restore):任务执行完成后,恢复线程原来的TTL值

3. 实现机制
// 伪代码展示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);}}
}
四、使用方式与示例
1. 基本使用
// 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();
2. 线程池集成

更优雅的方式是使用TtlExecutors包装线程池:

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());
});
3. 异步场景示例
// 初始化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. 初始化建议
// 推荐使用withInitial初始化
private static final TransmittableThreadLocal<SimpleDateFormat> DATE_FORMATTER = TransmittableThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
2. 内存管理

- 及时remove:任务完成后调用remove()避免内存泄漏
- 避免存储大对象:TTL变量应保持轻量级

try {// 使用TTL
} finally {ttlVariable.remove();
}
3. 与线程池配合
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(5);
// 包装线程池
ExecutorService ttlExecutor = TtlExecutors.getTtlExecutorService(executor);// 使用包装后的线程池
ttlExecutor.execute(() -> {// 可以获取TTL值
});
4. 性能考虑

- TTL会带来一定的性能开销(约5%)
- 高并发场景下应评估是否必要
- 考虑使用更轻量的解决方案(如方法参数传递)

六、常见问题与避坑指南
1. 内存泄漏

问题表现:线程池中的线程长期存活,TTL变量一直存在

解决方案:
 

try {// 业务代码
} finally {ttlVariable.remove();
}
2. 线程池未包装

问题表现:直接使用线程池提交任务,TTL值丢失

错误示例:
 

executor.execute(task); // 直接提交,TTL失效

正确做法:

executor.execute(TtlRunnable.get(task)); // 包装后提交
// 或
ttlExecutor.execute(task);
3. 与第三方框架集成

问题表现:Spring的@Async、Hystrix等框架中TTL失效

解决方案:
- 自定义线程池包装器
- 使用AOP拦截增强

@Bean
public Executor asyncExecutor() {ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();// 配置executorreturn TtlExecutors.getTtlExecutorService(executor.getThreadPoolExecutor());
}
4. 值覆盖问题

问题表现:多个任务共享线程时,TTL值被覆盖

解决方案:
- 确保每次任务执行后恢复原值(TTL已自动处理)
- 避免在任务中修改TTL值影响其他任务

七、适用场景
1. 分布式跟踪
// 设置traceId
TransmittableThreadLocal<String> traceId = new TransmittableThreadLocal<>();void processRequest(Request request) {traceId.set(request.getTraceId());// 异步处理不影响traceId传递asyncService.process(request);
}
2. 用户上下文传递
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();}
}
3. 多租户系统
// 租户上下文
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
}
4. 日志增强
// 日志上下文
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();}}
}
八、性能优化建议

1. 减少TTL变量数量:只将必要的数据放入TTL
2. 使用基本类型:避免复杂对象
3. 对象复用:对于频繁使用的对象,考虑对象池
4. 合理使用remove:长时间存活的线程池要定期清理

九、与其他技术对比
特性ThreadLocalInheritableThreadLocalTransmittableThreadLocal
线程隔离支持支持支持
父子线程传递不支持支持支持
线程池支持不支持不支持支持
执行前后自定义逻辑不支持不支持支持
性能开销中高
十、总结

TransmittableThreadLocal是解决线程池环境下上下文传递的强大工具,合理使用可以简化编程模型,但需要注意内存管理和性能影响。关键点:

1. 理解"捕获-传递-恢复"机制
2. 线程池必须通过TtlRunnable/TtlCallable或TtlExecutors包装
3. 及时清理避免内存泄漏
4. 评估性能影响,避免滥用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/78288.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下的I/O复用技术之epoll

I/O多路复用 指在单个线程或进程中&#xff0c;同时处理多个I/O操作的技术。 旨在提高程序处理多个并发I/O操作的能力&#xff0c;避免程序因等待某个I/O操作而被阻塞。在传统的I/O模型中当程序进行I/O操作时(如读取文件、接受网路数据等)&#xff0c;如果数据还未准备好&…

用 C 语言实现通用的冒泡排序算法

在日常编程中&#xff0c;排序算法是一个非常常见且重要的工具。虽然有许多排序算法可以选择&#xff0c;但如果你需要一个能够处理不同数据类型的排序算法&#xff0c;如何设计一个通用的排序算法呢&#xff1f;今天我们将实现一个通用的冒泡排序算法&#xff0c;支持不同数据…

C# 变量全解析:声明、初始化与使用

在多用途的编程语言中&#xff0c;程序存取数据是一项基础且关键的功能&#xff0c;而这一功能主要通过变量来实现。本文将全面深入地探讨 C# 中的变量&#xff0c;包括变量的种类、声明、初始化、自动初始化、多变量声明以及如何使用变量的值。 变量概述 变量是一个名称&…

Dify中的文本分词处理技术详解

Dify中的文本分词处理技术详解 引言核心架构概览索引处理器工厂 文本分词技术详解基础分词器增强型递归字符分词器固定分隔符文本分词器递归分割算法 索引处理器中的分词应用特殊索引处理器的分词特点问答索引处理器父子索引处理器 分词技术的应用场景技术亮点与优势总结 引言 …

如何打包python程序为可执行文件

将 Python 程序打包为可执行文件是一个常见需求&#xff0c;尤其是在希望将应用程序分享给不具备 Python 环境的用户时。以下是使用 PyInstaller 工具将 Python 程序打包为可执行文件的步骤。 步骤 1&#xff1a;安装 PyInstaller 如果您还没有安装 PyInstaller&#xff0c;请…

美团Java后端二面面经!

场景题是面试的大头&#xff0c;建议好好准备 Q. [美团]如何设计一个外卖订单的并发扣减库存系统&#xff1f; Q.[美团]为啥初始标记和重新标记需要STW&#xff1f; Q.[美团]骑手位置实时更新&#xff0c;如何保证高并发写入&#xff1f; Q.[美团]订单表数据量过大导致查询…

在应用运维过程中,业务数据修改的证据留存和数据留存

在应用运维过程中,业务数据修改的证据留存和数据留存至关重要,以下是相关介绍: 一、证据留存 操作日志记录 : 详细记录每一次业务数据修改的操作日志,包括操作人员、操作时间、修改内容、修改前后数据的对比等。例如,某公司业务系统中,操作日志会精确记录员工小张在 2…

Eigen迭代求解器类

1. 迭代求解器核心类概览 Eigen 提供多种迭代法求解稀疏线性方程组 AxbAxb&#xff0c;适用于大规模稀疏矩阵&#xff1a; 求解器类适用矩阵类型算法关键特性ConjugateGradient对称正定&#xff08;SPD&#xff09;共轭梯度法&#xff08;CG&#xff09;高精度&#xff0c;内…

ORACLE数据库备份入门:第四部分:2-备份场景举例

下面以4个常见的场景为例&#xff0c;介绍如何规划备份方案。备份方案没有标准答案&#xff0c;需要根据实现情况来制定&#xff0c;也和管理员的个人使用习惯有很大相关性。 1 交易型数据库备份 以银行的交易系统为例&#xff0c;除了前一章节提到的关于RPO和RTO的指标外&am…

小白如何学会完整挪用Github项目?(以pix2pix为例)

[目录] 0.如何完整地复现/应用一个Github项目 1.建立适用于项目的环境 2.数据准备与模型训练阶段 3.训练过程中的一些命令行调试必备知识0.如何完整地复现/应用一个Github项目 前日在健身房的组间同一位好友交流时&#xff0c;得到了一个一致结论—— ** Github \texttt{Githu…

蓝桥杯 5. 交换瓶子

交换瓶子 原题目链接 题目描述 有 N 个瓶子&#xff0c;编号为 1 ~ N&#xff0c;放在架子上。 例如有 5 个瓶子&#xff0c;当前排列为&#xff1a; 2 1 3 5 4每次可以拿起 2 个瓶子&#xff0c;交换它们的位置。 要求通过若干次交换&#xff0c;使得瓶子的编号从小到大…

Linux 系统渗透提权

Linux 系统渗透提权 比赛题库-Linux 系统渗透提权 文章目录 Linux 系统渗透提权比赛题库-Linux 系统渗透提权 前言一、解题过程1.使用渗透机对服务器信息收集&#xff0c;并将服务器中 SSH 服务端口号作为 flag 提 交&#xff1b;2.使用渗透机对服务器信息收集&#xff0c;并将…

华为OD机试真题——查找接口成功率最优时间段(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C、GO六种语言的最佳实现方式&#xff1b; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析&#xff1b; 本文收录于专栏&#xff1a;《2025华为OD真题目录…

华为OD机试真题——绘图机器(2025A卷:100分)Java/python/JavaScript/C++/C/GO最佳实现

2025 A卷 100分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析&#xff1b; 并提供Java、python、JavaScript、C、C语言、GO六种语言的最佳实现方式&#xff01; 本文收录于专栏&#xff1a;《2025华为OD真题目录全流程解析/备考攻略/经验…

基于 Python(selenium) 的百度新闻定向爬虫:根据输入的关键词在百度新闻上进行搜索,并爬取新闻详情页的内容

该项目能够根据输入的关键词在百度新闻上进行搜索,并爬取新闻详情页的内容。 一、项目准备 1. 开发环境配置 操作系统:支持 Windows、macOS、Linux 等主流操作系统,本文以 Windows 为例进行说明。Python 版本:建议使用 Python 3.8 及以上版本,以确保代码的兼容性和性能。…

MySQL表的操作 -- 表的增删改查

目录 1. 表的创建2. 表的查看3. 表的修改4. 表的删除5. 总结 1. 表的创建 1.查看字符集及效验规则 2. 表的创建 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎;创建用户表1 创建用…

如何解决极狐GitLab 合并冲突?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 合并冲突 (BASIC ALL) 合并冲突发生在合并请求的两个分支&#xff08;源分支和目标分支&#xff09;对相同代码行进行了不同…

oracle不同数据库版本的自增序列

-- 查看数据库版本 SELECT * FROM v$version WHERE banner LIKE Oracle%; 1. Oracle 12c及以上版本支持 id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY, id NUMBER GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1) PRIMARY KEY, -- 语法 id NUMBER GENER…

VIC-3D非接触全场应变测量系统用于小尺寸测量之电子元器件篇—研索仪器DIC数字图像相关技术

在5G通信、新能源汽车电子、高密度集成电路快速迭代的今天&#xff0c;电子元件的尺寸及连接工艺已进入亚毫米级竞争阶段&#xff0c;这种小尺寸下的力学性能评估对测量方式的精度有更高的要求&#xff0c;但传统应变测量手段常因空间尺寸限制及分辨率不足难以捕捉真实形变场。…

pod 创建私有库指南

步骤 参考&#xff1a;iOS Pod 私有库创建指南-百度开发者中心 下面主要是对参考链接里面的解释&#xff1a; 创建两个仓库&#xff1a; 一个叫podframe.git&#xff0c;用来存放自定义的framework&#xff0c;比如TestPodFrame.framework一个叫podspec.git&#xff0c;用来…