java线程池开源组件_阿里开源支持缓存线程池的ThreadLocal Transmittable ThreadLocal(TTL)...

文章介绍了在使用线程池时如何实现ThreadLocal值的传递,通过阿里开源的TransmittableThreadLocal(TTL)组件,支持在任务提交给线程池时将上下文传递到任务执行时,适用于分布式跟踪、日志收集等场景。提供了修饰Runnable、Callable、线程池以及使用Java Agent修饰JDK线程池实现类的示例。
摘要由CSDN通过智能技术生成

功能

在使用线程池等会缓存线程的组件情况下,提供ThreadLocal值的传递功能。

JDK的InheritableThreadLocal类可以完成父子线程值的传递。 但对于使用线程池等会缓存线程的组件的情况,线程由线程池创建好,并且线程是缓存起来反复使用的;这时父子线程关系的上下文传递已经没有意义,应用中要做上下文传递,实际上是在把 任务提交给线程池时的上下文传递到 任务执行时。

欢迎

需求场景

在ThreadLocal的需求场景即是TTL的潜在需求场景,如果你的业务需要『在使用线程池等会缓存线程的组件情况下传递ThreadLocal』则是TTL目标场景。

下面是几个典型场景例子。

分布式跟踪系统

应用容器或上层框架跨应用代码给下层SDK传递信息

日志收集记录系统上下文

各个场景的展开说明参见子文档 需求场景。

User Guide

比InheritableThreadLocal,添加了protected方法copy,用于定制 任务提交给线程池时的上下文传递到 任务执行时时的拷贝行为,缺省是传递的是引用。

具体使用方式见下面的说明。

1. 简单使用

父线程给子线程传递值。

示例代码:

// 在父线程中设置

TransmittableThreadLocal parent = new TransmittableThreadLocal();

parent.set("value-set-in-parent"); // =====================================================

// 在子线程中可以读取, 值是"value-set-in-parent"

String value = parent.get();

但对于使用了异步执行(往往使用线程池完成)的情况,线程由线程池创建好,并且线程是缓存起来反复使用的。

这时父子线程关系的上下文传递已经没有意义,应用中要做上下文传递,实际上是在把 任务提交给线程池时的上下文传递到任务执行时。 解决方法参见下面的这几种用法。

2. 保证线程池中传递值

2.1 修饰Runnable和Callable

示例代码:

TransmittableThreadLocal parent = new TransmittableThreadLocal();

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 parent = new TransmittableThreadLocal();

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();

整个过程的完整时序图

2c4ab45d8d080277ad16c2d81a69a257.png

2.2 修饰线程池

省去每次Runnable和Callable传入线程池时的修饰,这个逻辑可以在线程池中完成。

getTtlExecutor:修饰接口Executor

getTtlExecutorService:修饰接口ExecutorService

ScheduledExecutorService:修饰接口ScheduledExecutorService

示例代码:

ExecutorService executorService = ... // 额外的处理,生成修饰了的对象

executorService executorService = TtlExecutors.getTtlExecutorService(executorService);

TransmittableThreadLocal parent = new TransmittableThreadLocal();

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 parent = new TransmittableThreadLocal();

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();

目前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

Maven依赖

示例:

com.alibaba

transmittable-thread-local

2.0.0

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的出错信息,但不影响运行。

更多文档

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值