ThreadLocal、InheritableThreadLocal、TransmittableThreadLocal的介绍及使用场景

本文只对三种变量使用做介绍,实现原理待下篇文章讲解。

1. ThreadLocal的使用

介绍:ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题,如下图所示
在这里插入图片描述
继承关系:
请添加图片描述

    private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public static void main(String[] args) {
    	CONTEXT_HOLDER.set("initial");
        // 线程1
        new Thread(() -> {
            CONTEXT_HOLDER.set("thread1");
            System.out.println("thread1 threadLocal=====" + CONTEXT_HOLDER.get());
        }).start();

        // 线程2
        new Thread(() -> System.out.println("thread2 threadLocal=====" + CONTEXT_HOLDER.get())).start();

        // 线程3
        new Thread(() -> {
            System.out.println("thread3 threadLocal=====" + CONTEXT_HOLDER.get());
        }).start();
    }

如上代码,线程1对threadlocal变量做了修改,分别输出线程1、线程2、线程3的变量值结果如下:

thread1 threadLocal=====thread1
thread2 threadLocal=====initial
thread3 threadLocal=====initial

由结果可以看出,线程1对ThreadLocal值做的修改,不会影响线程2、线程3的ThreadLocal值,由此可保证线程安全。

问题:
代码中若存在异步操作时,ThreadLocal变量无法传递给子线程

   private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();

    public static void main(String[] args) {
        // 主线程
        CONTEXT_HOLDER.set("main thread");
        System.out.println("main thread threadLocal=====" + CONTEXT_HOLDER.get());

        // 子线程
        new Thread(() -> {
            System.out.println("child thread threadLocal=====" + CONTEXT_HOLDER.get());
        }).start();

    }

如上代码,主线程设置了ThreadLocal变量,分别输出主线程、子线程的ThreadLocal变量值

main thread threadLocal=====main thread
child thread threadLocal=====null

由此可见,的ThreadLocal变量无法由父(主)线程传递给子线程。由此引入InheritableThreadLocal

2. InheritableThreadLocal的使用

InheritableThreadLocal变量主要解决ThreadLocal变量无法进行父子线程传递的问题

   private static final InheritableThreadLocal<String> CONTEXT_HOLDER = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        // 主线程
        CONTEXT_HOLDER.set("main thread");
        System.out.println("main thread threadLocal=====" + CONTEXT_HOLDER.get());

        // 子线程
        new Thread(() -> {
            System.out.println("child thread threadLocal=====" + CONTEXT_HOLDER.get());
        }).start();

    }

如上代码,替换成InheritableThreadLocal后,主线程设置ThreadLocal变量,分别输出主线程、子线程的ThreadLocal变量值

main thread threadLocal=====main thread
child thread threadLocal=====main thread

由此可见,InheritableThreadLocal变量可进行父子线程之间的传递

问题:
子线程为新起线程时,InheritableThreadLocal变量值才可传递(具体需见InheritableThreadLocal父子线程的源码),若子线程为线程池复用的线程则无法进行传递

   private static final InheritableThreadLocal<String> CONTEXT_HOLDER = new InheritableThreadLocal<>();

    // 线程池大小为1 保证后续执行线程为已初始化的线程
    private static ExecutorService ioExecutor = new ThreadPoolExecutor(1, 1, 10L, TimeUnit.MINUTES, new ArrayBlockingQueue<>(50), new ThreadPoolExecutor.CallerRunsPolicy());

    @Scheduled(fixedDelay = 1000)
    public void test() {
        CONTEXT_HOLDER.set("main thread");
        System.out.println("main thread threadLocal=====" + CONTEXT_HOLDER.get());

        // (线程池)子线程
        ioExecutor.execute(() -> {
            System.out.println("child thread threadLocal=====" + CONTEXT_HOLDER.get());
            CONTEXT_HOLDER.set("clear thredlocal");
        });
    }

定时任务第一次执行输出结果

main thread threadLocal=====main thread
child thread threadLocal=====main thread

定时任务第二次执行输出结果

main thread threadLocal=====main thread
child thread threadLocal=====clear thredlocal

定时任务第三次执行输出结果

main thread threadLocal=====main thread
child thread threadLocal=====clear thredlocal

由上可知,InheritableThreadLocal变量在线程池线程初始化时,会进行变量值的传递,当线程复用时,无法进行变量值的传递

3. TransmittableThreadLocal的使用

需要引入阿里依赖:

     <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>transmittable-thread-local</artifactId>
            <version>2.12.1</version>
     </dependency>

线程池线程复用场景下,支持父子线程TransmittableThreadLocal变量的传递

     private static final TransmittableThreadLocal<String> CONTEXT_HOLDER = new TransmittableThreadLocal<>();

    // 线程池大小为1 保证后续线程为已初始化的线程
    private static ExecutorService ioExecutor = new ThreadPoolExecutor(1, 1, 10L, TimeUnit.MINUTES, new ArrayBlockingQueue<>(50), new ThreadPoolExecutor.CallerRunsPolicy());

    @Scheduled(fixedDelay = 1000)
    public void test() {
        CONTEXT_HOLDER.set("main thread");
        System.out.println("main thread threadLocal=====" + CONTEXT_HOLDER.get());

        // (线程池)子线程  注意提交需要使用TtlRunnable
        ioExecutor.execute(TtlRunnable.get(() -> {
            System.out.println("child thread threadLocal=====" + CONTEXT_HOLDER.get());
            CONTEXT_HOLDER.set("clear thredlocal");
        }));
    }

定时任务第一次执行输出结果

main thread threadLocal=====main thread
child thread threadLocal=====main thread

定时任务第二次执行输出结果

main thread threadLocal=====main thread
child thread threadLocal=====main thread

定时任务第二次执行输出结果

main thread threadLocal=====main thread
child thread threadLocal=====main thread

由结果可知,TransmittableThreadLocal变量在子线程(线程池复用线程)启动时,都会将主线程变量传递至子线程。

总结

threadlocal -> 父子线程变量无法传递
InheritableThreadLocal -> 父子线程会传递(子线程需为新建线程)
TransmittableThreadLocal -> 父子线程会传递(线程池(线程复用)亦可传递)

### InheritableThreadLocalTransmittableThreadLocal 的区别 #### 定义与基本功能 `InheritableThreadLocal` 是 `ThreadLocal` 的子类,允许子线程继承父线程的 `ThreadLocal` 变量副本。这意味着当创建一个新的子线程时,该子线程可以访问其父线程中设置的 `ThreadLocal` 值[^1]。 相比之下,`TransmittableThreadLocal` 并不是标准 Java 库的一部分,而是阿里巴巴开源的一个扩展库 Ttl (Transmittable Thread Local),旨在解决异步调用链路中的上下文传递问题。它不仅支持父子线程之间的值传递,还能够处理诸如线程池等复杂情况下的数据传输[^2]。 #### 使用场景差异 对于 `InheritableThreadLocal` 来说,在多线程编程中有特定需求的情况下非常有用——即希望某些配置或状态能够在新启动的子进程中自动获得来自父进程的相关信息而无需显式地重新设定这些属性。然而,这种机制仅限于直接由当前线程派生出来的后代线程之间有效[^3]。 另一方面,`TransmittableThreadLocal` 更适合用于更复杂的并发环境特别是涉及到第三方组件(比如执行器框架ExecutorService)、回调函数或者其他形式非阻塞操作的地方。因为在这种环境下普通的 `ThreadLocal` 或者甚至 `InheritableThreadLocal` 都无法正常工作来保持跨不同阶段的任务间的一致性和连续性[^4]。 ```java // InheritableThreadLocal 示例代码 public class MyRunnable implements Runnable { private static final InheritableThreadLocal<String> inheritableContext = new InheritableThreadLocal<>(); @Override public void run() { System.out.println(Thread.currentThread().getName() + " has value: " + inheritableContext.get()); } public static void main(String[] args) throws InterruptedException { inheritableContext.set("Main thread context"); Thread t = new Thread(new MyRunnable(), "Child Thread"); t.start(); t.join(); // Wait for the child thread to finish. } } ``` ```java // TransmittableThreadLocal 示例代码 import com.alibaba.ttl.TransmittableThreadLocal; public class TaskRunner { private static final TransmittableThreadLocal<String> ttlContext = new TransmittableThreadLocal<>(); public static void executeTask(Runnable task){ ExecutorService executor = Executors.newSingleThreadExecutor(); try{ ttlContext.set("TTL Context Value"); // Wrap and submit the task with TTL propagation support. executor.submit(Ttl.wrap(task)); } finally { executor.shutdown(); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值