TransmittableThreadLocal源码解析

ThreadLocal相信大家并不陌生,它是 Java 中用于实现线程封闭(Thread confinement)的一种机制,它允许每个线程拥有自己独立的变量副本,从而在多线程环境下实现数据的隔离性。然而,尽管 ThreadLocal 提供了便利,但也存在一些缺点。其中最显著的是在涉及线程池或者线程复用的情况下,ThreadLocal 变量的传递会遇到问题。

当线程池中的线程复用时,ThreadLocal 的值并不会被自动传递给新的线程。这导致在跨线程调用时,原始线程中设置的 ThreadLocal 变量无法被新线程访问,进而可能导致错误的行为或意外的结果。这种情况下,ThreadLocal 的作用变得相对受限,限制了其在特定多线程场景下的适用性。

针对这些 ThreadLocal 的局限性,TransmittableThreadLocal 应运而生。它是阿里巴巴开源的一个库,旨在解决 ThreadLocal 在线程池或者线程复用情况下的传递问题,能够有效地克服 ThreadLocal 的限制,使得在多线程环境下数据的传递更加可靠和灵活。接下来,我们将从源码角度深入探讨 TransmittableThreadLocal 的实现原理和解决方案,以及它是如何优雅地解决了 ThreadLocal 的不足之处。

一、ThreadLocal的局限性

我们运行下列两组代码,对比一下ThreadLocal和TransmittableThreadLocal的执行结果

csharp 复制代码
public class TestThreadLocal {
    public static void main(String[] args) {
        ThreadLocal<String> threadLocal = new ThreadLocal<>();
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 1; i <= 4; i++) {
            threadLocal.set("Java Boy"+i);
            System.out.println("主线程:"+threadLocal.get());
            executorService.execute(()->{
                System.out.println("---子线程---:"+threadLocal.get());
            });
        }
    }
}
csharp 复制代码
public class TestTransmittableThreadLocal {
    public static void main(String[] args) {
        ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>();
        ExecutorService executorService = Executors.newFixedThreadPool(2);
        //这一步很重要
        executorService = TtlExecutors.getTtlExecutorService(executorService);
        for (int i = 1; i <= 4; i++) {
            threadLocal.set("Java Boy"+i);
            System.out.println("主线程:"+threadLocal.get());
            executorService.execute(()->{
                System.out.println("子线程:"+threadLocal.get());
            });
        }
    }
}

当我们使用 ThreadLocal 时,子线程无法直接获取到父线程所设置的 ThreadLocal 变量。这种现象是合理的,因为 ThreadLocal 是与线程相关联的线程本地变量,所以子线程无法直接访问父线程的 ThreadLocal 变量。然而,在实际业务中,我们常常需要在多线程异步操作中使子线程能够获取到全局变量的情况。这种情况下,ThreadLocal 就无法满足我们的需求。在这种情况下,TransmittableThreadLocal 的出现能够很好地解决这个问题,允许子线程有效地获取到在父线程中设置的变量,从而实现了在多线程环境中全局变量的传递。

二、TransmittableThreadLocal源码解析

TransmittableThreadLocal github地址

首先我们看下TransmittableThreadLocal类

再看下set和get方法

可以看到,TransmittableThreadLocal继承了InheritableThreadLocal和ThreadLocal,虽然重写了set及get方法,但是最终这两个方法都是调用了父类中的方法,这样肯定是无法实现线程池变量传递的。

我们接着往下看,TransmittableThreadLocal是的使用姿势中对我们的线程池TtlExecutors.getTtlExecutorService,我们点进这个方法看一下

看到Wapper,很明显,这里是使用了装饰器模式,对我们的线程池进行了包装操作,直接看代码可能还不够清晰,我们进行debug断点调试吧。

代码执行到这里做了这几件事:TransmittableThreadLocal对线程池进行了包装,将扔到线程池的Runnable先组装成TtlRunnable,将父线程的TransmittableThreadLocal变量记录到TtlRunnable的capturedRef属性上,TtlRunnable重写了run方法,在执行Runnable.run()之前,将父线程中的变量值取了出来。

我们再接着往下看

这里将取出的父线程中的变量值captured赋给了子线程,从而成功实现了线程本地变量在父子线程中的传递。

三、总结

在本文中,我们对 TransmittableThreadLocal 核心源码进行了探讨,特别是其在实现线程传递过程中的核心机制。然而,TransmittableThreadLocal 的实现远不止所介绍的内容那么简单。在其背后涉及了更多深入的细节和技术原理,包括线程池中线程的管理、序列化和反序列化机制、内存模型等方面。

在深入学习 TransmittableThreadLocal 的源码过程中,你会发现其实现中涉及了对 Java 并发编程的精妙设计和高效实现。阅读源码不仅能够加深对其工作原理的理解,还能够培养我们解决复杂多线程问题的能力。

如果你对 Java 并发编程和线程传递机制感兴趣,强烈建议花时间深入研究 TransmittableThreadLocal 的源码和相关技术细节。通过阅读源码、调试和实验,你将更深入地了解其内部工作原理,并从中获得宝贵的经验和知识。

喜欢的可以点个关注,我将持续更新技术文章,谢谢。

相关推荐
cainiao0806051 分钟前
Java 大视界——Java 大数据在智慧交通智能停车诱导系统中的数据融合与实时更新
java·大数据·开发语言
sufu10657 分钟前
SpringAI更新:废弃tools方法、正式支持DeepSeek!
人工智能·后端
chxii8 分钟前
6.2字节流
java·开发语言
不务专业的程序员--阿飞17 分钟前
【SQL 如何解锁递归】
java·数据库·sql
嘵奇23 分钟前
Spring Boot拦截器详解:原理、实现与应用场景
java·spring boot·后端
八股文领域大手子24 分钟前
Java死锁排查:线上救火实战指南
java·开发语言·面试
jackson凌31 分钟前
【Java学习笔记】finalize方法
java·笔记·学习
fanTuanye34 分钟前
redis 缓存穿透,缓存击穿,缓存雪崩
java·redis·缓存
神秘的t1 小时前
Spring Web MVC————入门(2)
java·spring·mvc
开开心心就好1 小时前
高效全能PDF工具,支持OCR识别
java·前端·python·pdf·ocr·maven·jetty