README.md

    📌 Transmittable ThreadLocal(TTL) 📌

    Build Status Windows Build Status Coverage Status Maintainability Maven Central GitHub release
    Join the chat at https://gitter.im/alibaba/transmittable-thread-local GitHub issues Percentage of issues still open Average time to resolve an issue License

    📖 English Documentation | 📖 中文文档



    🔧 功能

    👉 在使用线程池等会池化复用线程的组件情况下,提供ThreadLocal值的传递功能,解决异步执行时上下文传递的问题。 一个Java标准库本应为框架/中间件设施开发提供的标配能力,本库功能聚焦 & 0依赖,支持Java 11/10/9/8/7/6。

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

    本库提供的TransmittableThreadLocal类继承并加强InheritableThreadLocal类,解决上述的问题,使用详见User Guide

    整个库包含TTL核心功能、线程池修饰及Agent支持(ExecutorService/ForkJoinPool),只有不到 800 SLOC代码行,非常精小。

    欢迎 👏

    🎨 需求场景

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

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

    1. 分布式跟踪系统
    2. 日志收集记录系统上下文
    3. 应用容器或上层框架跨应用代码给下层SDK传递信息

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

    👥 User Guide

    使用类TransmittableThreadLocal来保存值,并跨线程池传递。

    TransmittableThreadLocal继承InheritableThreadLocal,使用方式也类似。

    相比InheritableThreadLocal,添加了

    1. protected方法copy
      用于定制 任务提交给线程池时ThreadLocal值传递到 任务执行时 的拷贝行为,缺省传递的是引用。
    2. protected方法beforeExecute/afterExecute
      执行任务(Runnable/Callable)的前/后的生命周期回调,缺省是空操作。

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

    1. 简单使用

    父线程给子线程传递值。

    示例代码:

    // 在父线程中设置
    TransmittableThreadLocal<String> parent = new TransmittableThreadLocal<String>();
    parent.set("value-set-in-parent");
    
    // =====================================================
    
    // 在子线程中可以读取,值是"value-set-in-parent"
    String value = parent.get();

    这是其实是InheritableThreadLocal的功能,应该使用InheritableThreadLocal来完成。

    但对于使用线程池等会池化复用线程的组件的情况,线程由线程池创建好,并且线程是池化起来反复使用的;这时父子线程关系的ThreadLocal值传递已经没有意义,应用需要的实际上是把 任务提交给线程池时ThreadLocal值传递到 任务执行时

    解决方法参见下面的这几种用法。

    2. 保证线程池中传递值

    2.1 修饰RunnableCallable

    使用TtlRunnableTtlCallable来修饰传入线程池的RunnableCallable

    示例代码:

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

    上面演示了RunnableCallable的处理类似

    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 修饰线程池

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

    通过工具类com.alibaba.ttl.threadpool.TtlExecutors完成,有下面的方法:

    • getTtlExecutor:修饰接口Executor
    • getTtlExecutorService:修饰接口ExecutorService
    • getTtlScheduledExecutorService:修饰接口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或是线程池的代码。即可以做到应用代码 无侵入
    # 关于 无侵入 的更多说明参见文档Java Agent方式对应用代码无侵入

    示例代码:

    // ## 1. 框架上层逻辑,后续流程框架调用业务 ##
    TransmittableThreadLocal<String> context = new TransmittableThreadLocal<String>();
    context.set("value-set-in-parent");
    
    // ## 2. 应用逻辑,后续流程业务调用框架下层逻辑 ##
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    
    Runnable task = new Task("1");
    Callable call = new Call("2");
    executorService.submit(task);
    executorService.submit(call);
    
    // ## 3. 框架下层逻辑 ##
    // Task或是Call中可以读取,值是"value-set-in-parent"
    String value = context.get();

    Demo参见AgentDemo.java。执行工程下的脚本scripts/run-agent-demo.sh即可运行Demo。

    目前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 AgentJar要加到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

    Java Agent的使用方式在什么情况下TTL会失效

    由于RunnableCallable的修饰代码,是在线程池类中插入的。下面的情况会让插入的代码被绕过,传递会失效。

    • 用户代码中继承java.util.concurrent.ThreadPoolExecutorjava.util.concurrent.ScheduledThreadPoolExecutor, 覆盖了executesubmitschedule等提交任务的方法,并且没有调用父类的方法。
      修改线程池类的实现,executesubmitschedule等提交任务的方法禁止这些被覆盖,可以规避这个问题。
    • 目前,没有修饰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.5.1</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_511.7.0_45可以运行。
      # 1.7.0_45还是有JavaLaunchHelper的出错信息,但不影响运行。

    🗿 更多文档

    📚 相关资料

    Jdk core classes

    Java Agent

    👷 Contributors

    • Jerry Lee <oldratlee at gmail dot com> @oldratlee
    • Yang Fang <snoop.fy at gmail dot com> @driventokill
    • wuwen <wuwen.55 at aliyun dot com> @wuwen5
    • 牧瑾 <351450944 at qq dot com> @LNAmp
    • Your name here :-)

    项目简介

    🚀 Github 镜像仓库 🚀

    源项目地址

    https://github.com/alibaba/transmittable-thread-local

    发行版本

    当前项目没有发行版本

    贡献者 15

    全部贡献者

    开发语言

    • Kotlin 53.2 %
    • Java 42.1 %
    • Shell 4.6 %
    • HTML 0.1 %