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 的源码和相关技术细节。通过阅读源码、调试和实验,你将更深入地了解其内部工作原理,并从中获得宝贵的经验和知识。

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

相关推荐
Dola_Pan1 小时前
Linux文件IO(二)-文件操作使用详解
java·linux·服务器
wang_book1 小时前
Gitlab学习(007 gitlab项目操作)
java·运维·git·学习·spring·gitlab
蜗牛^^O^2 小时前
Docker和K8S
java·docker·kubernetes
从心归零3 小时前
sshj使用代理连接服务器
java·服务器·sshj
IT毕设梦工厂4 小时前
计算机毕业设计选题推荐-在线拍卖系统-Java/Python项目实战
java·spring boot·python·django·毕业设计·源码·课程设计
Ylucius4 小时前
动态语言? 静态语言? ------区别何在?java,js,c,c++,python分给是静态or动态语言?
java·c语言·javascript·c++·python·学习
凡人的AI工具箱5 小时前
AI教你学Python 第11天 : 局部变量与全局变量
开发语言·人工智能·后端·python
是店小二呀5 小时前
【C++】C++ STL探索:Priority Queue与仿函数的深入解析
开发语言·c++·后端
七夜zippoe5 小时前
分布式系统实战经验
java·分布式
canonical_entropy5 小时前
金蝶云苍穹的Extension与Nop平台的Delta的区别
后端·低代码·架构