TransmittableThreadLocal的实现机制和原理

1 前言
前面我看过了 ThreadLocal的实现机制和原理 以及 InheritableThreadLocal的实现机制和原理 两种类型的 ThreadLocal,前者是普通的,后者是在前者的基础上套了一层父子线程关系,当使用后者的时候,会在线程创建的时候,浅拷贝一份父线程的变量值。那么今天空了,我来看看另外一种 ThreadLocal:TransmittableThreadLocal。
2 TransmittableThreadLocal
2.1 TransmittableThreadLocal 的认识
TransmittableThreadLocal(TTL)是阿里巴巴开源的一个 Java 库,用于解决 ThreadLocal 在多线程环境下的一些问题,尤其是在使用线程池等场景下可能出现的问题。与普通的 ThreadLocal 不同,TTL 具有以下特点:

  • 线程池透传性:在使用线程池执行任务时,TTL 可以透传 ThreadLocal 的值,确保后续线程能够正确访问前线程设置的 TransmittableThreadLocal 变量值。
  • 线程池隔离性:TTL 在多线程环境下能够确保每个线程都有独立的 TransmittableThreadLocal值,避免了线程池重用线程时可能出现的数据污染问题,比如线程池执行前从父线程继承的变量,不管是执行中变没变,下次执行任务的时候,还是会和父线程保持一致。
  • 资源自动清理:TTL 支持自动清理 TransmittableThreadLocal 值,避免了可能导致内存泄漏的问题。
  • 兼容性:TTL 兼容原生 ThreadLocal 的语法和用法,可以直接替换原生 ThreadLocal 使用,而无需修改现有代码。

2.2 TransmittableThreadLocal 的使用
TTL 通常用于需要在线程池中执行任务,并且需要在任务之间传递 ThreadLocal 值的场景。例如,在 Web 应用中,可能需要在异步任务中访问当前用户的会话信息,而使用 TTL 可以确保子线程能够正确访问父线程设置的会话信息。TransmittableThreadLocal在使用线程池等会池化复用线程的执行组件情况下,提供ThreadLocal值的传递功能(把任务提交给线程池时的ThreadLocal值传递到任务执行时),解决异步执行时上下文传递的问题。我们来简单体验一下:

// TTL
private static final TransmittableThreadLocal<Integer> tl = new TransmittableThreadLocal<>();
public static void main(String[] args) {
    // 父线程设置变量 1
    tl.set(1);
    new Thread(() -> {
        System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get()));
    }).start();
    new Thread(() -> {
        System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get()));
    }).start();
}

这么一看貌似跟我们的 ITL 没什么区别是吧。我们来看一个线程池的例子:

public class Demo {

    private static final TransmittableThreadLocal<Integer> tl = new TransmittableThreadLocal<>();
    
    private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
            2,
            2,
            10,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100)
    );

    public static void main(String[] args) {
        // 父线程设置变量 1
        tl.set(1);       

poolExecutor.execute(() -> { // 更改当前线程中的值 tl.set(2); System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); });
poolExecutor.execute(()
-> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); });
poolExecutor.execute(()
-> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); });
poolExecutor.execute(()
-> { System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get())); }); } }

我们看普通情况下,当某个线程改变了 TTL 的值后,下次该线程执行任务的时候,TTL 的值就是改变后的了。这里需要引入一下 TTL 里的线程池,我们再看:

public class Demo {

    private static final TransmittableThreadLocal<Integer> tl = new TransmittableThreadLocal<>();

    private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
            2,
            2,
            10,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100)
    );
    private static final Executor ttlExecutor = TtlExecutors.getTtlExecutor(poolExecutor);

    public static void main(String[] args) {
        // 父线程设置变量 1
        tl.set(1);

        ttlExecutor.execute(() -> {
            // 更改当前线程中的值
            tl.set(2);
            System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get()));
        });
        ttlExecutor.execute(() -> {
            System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get()));
        });
        ttlExecutor.execute(() -> {
            System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get()));
        });
        ttlExecutor.execute(() -> {
            System.out.println(String.format("子线程:%s,获取值=%s", Thread.currentThread().getName(), tl.get()));
        });

    }
}

2.4 应用场景
TransmittableThreadLocal用来实现线程间的参数传递,经典应用场景如下:
(1)分布式跟踪系统 或 全链路压测(即链路打标)
(2)日志收集记录系统上下文
(3)Session级 Cache
(4)应用容器或上层框架跨应用代码给下层 SDK传递信息
2.5 ThreadLocal、InheritableThreaLocal与TransmittableThreadLocal的比较
ThreadLocal、InheritableThreadLocal与TransmittableThreadLocal在Java中都是用于处理线程局部变量的工具,但它们在使用场景和特性上有所不同。
(1)ThreadLocal
ThreadLocal是Java中一个非常重要的线程技术,它为每个线程提供了它自己的变量副本,使得线程间无法相互访问对方的变量,从而避免了线程间的竞争和数据泄露问题。适用于需要在线程内部存储和获取数据,且不希望与其他线程共享数据的场景。
(2)InheritableThreadLocal
InheritableThreadLocal是ThreadLocal的一个子类,它包含了ThreadLocal的所有功能,并扩展了ThreadLocal的功能。 允许父线程中的InheritableThreadLocal变量的值被子线程继承。当创建一个新的线程时,这个新线程可以访问其父线程中InheritableThreadLocal变量的值。
(3)TransmittableThreadLocal
TransmittableThreadLocal是阿里巴巴开源的一个框架,用于解决在使用线程池等场景下,ThreadLocal变量无法跨线程传递的问题。能够在多线程传递中保持变量的传递性,确保在父线程和子线程之间正确传递ThreadLocal变量。简单的来说就是:ThreadLocal适用于线程内部的数据存储和访问,确保数据在线程间的隔离。InheritableThreadLocal适用于需要在父线程和子线程间传递数据的场景,实现数据的继承。TransmittableThreadLocal则是为了解决在使用线程池等场景下,ThreadLocal变量无法跨线程传递的问题,实现数据的跨线程传递。

 

参考博客:https://www.cnblogs.com/kukuxjx/p/18188062

posted @ 2024-12-09 11:13  郭慕荣  阅读(22)  评论(0编辑  收藏  举报