阿里开源支持缓存线程池的ThreadLocal Transmittable ThreadLocal(TTL)
功能
在使用线程池等会缓存线程的组件情况下,提供ThreadLocal值的传递功能。
JDK的InheritableThreadLocal类可以完成父子线程值的传递。 但对于使用线程池等会缓存线程的组件的情况,线程由线程池创建好,并且线程是缓存起来反复使用的;这时父子线程关系的上下文传递已经没有意义,应用中要做上下文传递,实际上是在把 任务提交给线程池时的上下文传递到 任务执行时。
本库提供的TransmittableThreadLocal类继承并加强InheritableThreadLocal类,解决上述的问题,使用详见User Guide。
欢迎
建议和提问,提交Issue
贡献和改进,Fork后提通过Pull Request贡献代码
需求场景
在ThreadLocal的需求场景即是TTL的潜在需求场景,如果你的业务需要『在使用线程池等会缓存线程的组件情况下传递ThreadLocal』则是TTL目标场景。
下面是几个典型场景例子。
分布式跟踪系统
应用容器或上层框架跨应用代码给下层SDK传递信息
日志收集记录系统上下文
各个场景的展开说明参见子文档 需求场景。
User Guide
使用类TransmittableThreadLocal来保存上下文,并跨线程池传递。
TransmittableThreadLocal继承InheritableThreadLocal,使用方式也类似。
比InheritableThreadLocal,添加了protected方法copy,用于定制 任务提交给线程池时的上下文传递到 任务执行时时的拷贝行为,缺省是传递的是引用。
具体使用方式见下面的说明。
1. 简单使用
父线程给子线程传递值。
示例代码:
// 在父线程中设置
TransmittableThreadLocal<String> parent = new TransmittableThreadLocal<String>();
parent.set("value-set-in-parent"); // =====================================================
// 在子线程中可以读取, 值是"value-set-in-parent"
String value = parent.get();
这是其实是InheritableThreadLocal的功能,应该使用InheritableThreadLocal来完成。
但对于使用了异步执行(往往使用线程池完成)的情况,线程由线程池创建好,并且线程是缓存起来反复使用的。
这时父子线程关系的上下文传递已经没有意义,应用中要做上下文传递,实际上是在把 任务提交给线程池时的上下文传递到任务执行时。 解决方法参见下面的这几种用法。
2. 保证线程池中传递值
2.1 修饰Runnable和Callable
使用com.alibaba.ttl.TtlRunnable和com.alibaba.ttl.TtlCallable来修饰传入线程池的Runnable和Callable。
示例代码:
TransmittableThreadLocal<String> parent = new TransmittableThreadLocal<String>();
parent.set("value-set-in-parent");
Runnable task = new Task("1"); // 额外的处理,生成修饰了的对象
ttlRunnable Runnable ttlRunnable = TtlRunnable.get(task);
executorService.submit(ttlRunnable); // =====================================================
// Task中可以读取, 值是"value-set-in-parent"
String value = parent.get();
上面演示了Runnable,Callable的处理类似
TransmittableThreadLocal<String> parent = new TransmittableThreadLocal<String>();
parent.set("value-set-in-parent"); Callable call = new Call("1"); // 额外的处理,生成修饰了的对象
ttlCallable Callable ttlCallable = TtlCallable.get(call);
executorService.submit(ttlCallable); // =====================================================
// Call中可以读取, 值是"value-set-in-parent" String value = parent.get();
整个过程的完整时序图

2.2 修饰线程池
省去每次Runnable和Callable传入线程池时的修饰,这个逻辑可以在线程池中完成。
通过工具类com.alibaba.ttl.threadpool.TtlExecutors完成,有下面的方法:
getTtlExecutor:修饰接口Executor
getTtlExecutorService:修饰接口ExecutorService
ScheduledExecutorService:修饰接口ScheduledExecutorService
示例代码:
ExecutorService executorService = ... // 额外的处理,生成修饰了的对象
executorService executorService = TtlExecutors.getTtlExecutorService(executorService);
TransmittableThreadLocal<String> parent = new TransmittableThreadLocal<String>();
parent.set("value-set-in-parent");
Runnable task = new Task("1"); Callable call = new Call("2");
executorService.submit(task);
executorService.submit(call); // =====================================================
// Task或是Call中可以读取, 值是"value-set-in-parent"
String value = parent.get();
2.3 使用Java Agent来修饰JDK线程池实现类
这种方式,实现线程池的传递是透明的,代码中没有修饰Runnable或是线程池的代码。
# 即可以做到应用代码 无侵入,后面文档有结合实际场景的架构对这一点的说明。
示例代码:
// 框架代码
TransmittableThreadLocal<String> parent = new TransmittableThreadLocal<String>();
parent.set("value-set-in-parent"); // 应用代码
ExecutorService executorService = Executors.newFixedThreadPool(3);
Runnable task = new Task("1");
Callable call = new Call("2");
executorService.submit(task);
executorService.submit(call); // =====================================================
// Task或是Call中可以读取, 值是"value-set-in-parent"
String value = parent.get();
Demo参见AgentDemo.java。
目前Agent中,修饰了jdk中的两个线程池实现类(实现代码在TtlTransformer.java):
java.util.concurrent.ThreadPoolExecutor
java.util.concurrent.ScheduledThreadPoolExecutor
在Java的启动参数加上:
-Xbootclasspath/a:/path/to/transmittable-thread-local-2.x.x.jar
-javaagent:/path/to/transmittable-thread-local-2.x.x.jar
注意:
Agent修改是JDK的类,类中加入了引用TTL的代码,所以TTL Agent的Jar要加到bootclasspath上。
Java命令行示例如下:
java -Xbootclasspath/a:transmittable-thread-local-2.0.0.jar \
-javaagent:transmittable-thread-local-2.0.0.jar \
-cp classes \
com.alibaba.ttl.threadpool.agent.demo.AgentDemo
有Demo演示『使用Java Agent来修饰线程池实现类』,执行工程下的脚本run-agent-demo.sh即可运行Demo。
Java Agent的使用方式在什么情况下TTL会失效
由于Runnable和Callable的修饰代码,是在线程池类中插入的。下面的情况会让插入的代码被绕过,传递会失效。
用户代码中继承java.util.concurrent.ThreadPoolExecutor和java.util.concurrent.ScheduledThreadPoolExecutor, 覆盖了execute、submit、schedule等提交任务的方法,并且没有调用父类的方法。
修改线程池类的实现,execute、submit、schedule等提交任务的方法禁止这些被覆盖,可以规避这个问题。目前,没有修饰java.util.Timer类,使用Timer时,TTL会有问题。
Java API Docs
当前版本的Java API文档地址: http://alibaba.github.io/transmittable-thread-local/apidocs/
Maven依赖
示例:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>transmittable-thread-local</artifactId>
<version>2.0.0</version>
</dependency>
可以在 search.maven.org 查看可用的版本。
FAQ
Mac OS X下,使用javaagent,可能会报JavaLaunchHelper的出错信息。
JDK Bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=8021205
可以换一个版本的JDK。我的开发机上1.7.0_40有这个问题,1.6.0_51、1.7.0_45可以运行。
# 1.7.0_45还是有JavaLaunchHelper的出错信息,但不影响运行。
更多文档
阿里开源支持缓存线程池的ThreadLocal Transmittable ThreadLocal(TTL)的更多相关文章
- (CSDN 迁移) JAVA多线程实现-可回收缓存线程池(newCachedThreadPool)
在前两篇博客中介绍了单线程化线程池(newSingleThreadExecutor).可控最大并发数线程池(newFixedThreadPool).下面介绍的是第三种newCachedThreadPo ...
- 0041 Java学习笔记-多线程-线程池、ForkJoinPool、ThreadLocal
什么是线程池 创建线程,因为涉及到跟操作系统交互,比较耗费资源.如果要创建大量的线程,而每个线程的生存期又很短,这时候就应该使用线程池了,就像数据库的连接池一样,预先开启一定数量的线程,有任务了就将任 ...
- 8、java5线程池之动态缓存线程池newCachedThreadPool
JDK文档描述 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们.对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能.调用 execute 将重用以前构造的线程 ...
- 常见线程池之 newCacheThreadPool 缓存线程池 简单使用
package com.aaa.threaddemo; import java.util.concurrent.BlockingQueue; import java.util.concurrent.E ...
- cachedThreadPool缓存线程池
package com.loan.modules.common.util; import java.util.concurrent.BlockingQueue; import java.util.co ...
- 阿里开源的缓存框架JetCache
之前一直在用Spring Cache进行接口数据的缓存,主要是Spring Cache在对具体key缓存失效时间的设置不是很方法,还要自己去扩展,无意中发现了阿里的JetCache.大部分的需求都能满 ...
- 【转】线程池体系介绍及从阿里Java开发手册学习线程池的正确创建方法
jdk1.7中java.util.concurrent.Executor线程池体系介绍 java.util.concurrent.Executor : 负责线程的使用与调度的根接口 |–Execut ...
- 阿里P7告诉你什么是java并发包、线程池、锁
并发包 java.util.concurrent从jdk1.5开始新加入的一个包,致力于解决并发编程的线程安全问题,使用户能够更为快捷方便的编写多线程情况下的并发程序. 同步容器 同步容器只有包括Ve ...
- (CSDN 迁移) JAVA多线程实现-支持定时与周期性任务的线程池(newScheduledThreadPool)
前几篇文章中分别介绍了 单线程化线程池(newSingleThreadExecutor) 可控最大并发数线程池(newFixedThreadPool) 可回收缓存线程池(newCachedThread ...
随机推荐
- opengl 4.5 中文api 链接
https://www.cnblogs.com/wiki3d/p/opengl_a.html
- docker在Windows环境下的安装
Windows环境下安装 docker有两种安装包 一.Docker for Windows(目前只能在 64 位的 Windows10 专业版.企业版.教育版下才能安装) 二.Docker Tool ...
- Ubuntu系统---安装English版本之后的一些工作
Ubuntu系统---安装English版本之后的一些工作 安装完U ...
- P3183 [HAOI2016]食物链[拓扑/记忆化搜索]
题目来源:洛谷 题目描述 如图所示为某生态系统的食物网示意图,据图回答第1小题现在给你n个物种和m条能量流动关系,求其中的食物链条数.物种的名称为从1到n编号M条能量流动关系形如a1 b1a2 b2a ...
- 模糊查询的sql语句
Java程序中使用的sql语句一直不明白是什么作用,在网上搜索了一些资料,看到一篇博客,稍微解答了具体每条代码的作用,因为作者加了详细的注解,可以作为参考 <JavaWeb dao层条件查询(模 ...
- linux实操_rpm包和yum包
rpm包的简单查询指令: 查询已安装的rpm列表 rpm -qa | grep xxx 查询火狐浏览器 查询安装的rpm包软件的信息 查询rpm软件包的文件安装在哪里 查询文件属于哪个软件包 卸载rp ...
- django2为什么要用序列化
https://www.jianshu.com/p/1e404604d5c3 结论:如果没用序列化,Django默认将数据进行多次转换才能提取我们所需数据,出现关联时我们还需进一步对数据进行查询,操作 ...
- app连接线上数据库进行本地接口测试
1.将开发环境下数据库配置改为生产环境下的数据库连接 2.备份生产环境下的数据库数据以及结构,使用Postman请求开发(本地)环境下的接口 3.打开手机上安装的线上app改动接口时查看app是否发生 ...
- SqlBulkCopy插入时:来自数据源的 String 类型的给定值不能转换为指定目标列的类型 int。 ---> System.FormatException: 将参数值从 String 转换到 Int32 失败。 ---> System.FormatException: 输入字符串的格式不正确。
偶尔间看到的 记录下:https://bbs.csdn.net/topics/390430064
- Python 练习实例2
Python 练习实例2 题目:企业发放的奖金根据利润提成.利润(I)低https://www.xuanhe.net/于或等于10万元时,奖金可提10%:利润高于10万元,低于20万元时,低于10万元 ...