InheritableThreadLocal——父线程传递本地变量到子线程的解决方式及分析

    上一个博客提到ThreadLocal变量的基本使用方式,可以看出ThreadLocal是相对于每一个线程自己使用的本地变量,但是在实际的开发中,有这样的一种需求:父线程生成的变量需要传递到子线程中进行使用,那么在使用ThreadLocal似乎就解决不了这个问题,难道这个业务就没办法使用这个本地变量了吗?答案肯定是否定的,ThreadLocal有一个子类InheritableThreadLocal就是为了解决这个问题而产生的,使用这个变量就可以轻松的在子线程中依旧使用父线程中的本地变量。

 

--> InheritableThreadLocal基本的使用和分析:

如图上的代码所示,创建一个InheritableThreadLocal变量,并在main主线程中对该变量进行初始化,然后新建一个子线程直接获取刚刚初始化的变量值,代码执行的实际效果如下:

运行的结果可以看出,使用该变量子线程可以使用父线程中的本地变量值,那么具体的原因是什么呢?还是从源码一看究竟。

 

--> InheritableThreadLocal源码浅析:

首先看一下该类的目录结构,继承自ThreadLocal,并且重写了父类的方法:createMap(),getMap(),childValue();

 

其次当主线程中对该变量进行set操作的时候,和ThreadLocal一样会初始化一个ThreadLocalMap对实际的变量值进行存储,以ThreadLocal为key,值为value,如果有多个ThreadLocal变量也都是存储在这个Map中。该Map使用的是HashMap的原理进行数据的存储,但是和ThreadLocal有一点差别,因为其覆写了createMap的方法。

 

 

再将目光转移到线程Thread类中,可以看出Thread类维护了两个成员变量,ThreadLocal以及InheritableThreadLocal,数据类型都是ThreadLocalMap.这也就解释了为什么这个变量是线程私有的。但是如果要知道为什么父子线程的变量传递,那就继续看一下源码。当我们在主线程中开一个新的子线程的时候,开始会new一个新的Thread,具体的实现也就是在这里。

经过调用init方法可以看出,方法中存在对InheritableThreadLocal的操作。获取的父线程也就是当前实际开辟线程的主线程。

当父线程中的inheritableThreadLocal被赋值时,会将当前线程的inheritableThreadLocal变量进行createInheritedMap(),看一下这个方法的具体实现,它会继续调用ThreadLocalMap(parentMap),主要的目的是父线程的变量值赋值给子线程。这里直接改变的是Entry[],因为ThreadLocalMap只是一个类名,具体数据存储和操作是使用内部的数组搭配Hash算法和Entry内部类实现。

 

代码看到这里,对于为什么父线程的InheritableThreadLocal变量可以传递给子线程的原因应该已经清晰了。

 

--> InheritableThreadLocal和线程池搭配使用存在的问题:

首先创建一个线程池,设置其固定大小为1,调用这个线程池两次,在此之前分别对主线程中的InheritableThreadLocal进行赋值操作,观察运行的结果。

 

 

两次调用获取的值是一开始赋值的值,因为线程池中是缓存使用过的线程,当线程被重复调用的时候并没有再重新初始化init()线程,而是直接使用已经创建过的线程,所以这里的值并不会被再次操作。因为实际的项目中线程池的使用频率非常高,每一次从线程池中取出线程不能够直接使用之前缓存的变量,所以要解决这一个问题,网上大部分是推荐使用alibaba的开源项目transmittable-thread-local。具体可以自行了解,但是笔者认为需要弄清楚线程池和主线程以及本地变量直接的详细关系才可以更好的对这个项目有所理解。

 

--> 线程池扩展源码阅读

从构造线程池到使用线程池的过程:

1.创建一个线程池,其内部是对线程池基本的参数进行封装。

 

 

如上的构造方法可以看出,实际对线程进行初始化的接口是ThreadFactory,当线程池执行submit或者execute操作的时候,会对线程进行实际的构造,源码选用submit(Runable)进行阅读。

执行submit的时候首先进入方法newTaskFor();

实际是通过FutureTask对任务类进行封装,并且初始化的状态是NEW,代码跟进如下:

 

其次调用的是execute()方法:

如上的方法中,ctl相当于一个记录当前活跃线程数量的类,在ThreadLocalExcutor中被初始化,

所以在execute的代码中,首先判断正在执行任务的线程数量是否小于设置的线程数,来决定是否创建新的线程或者等待被执行。如果可以添加通过addWorker()进行线程的创建和添加,这也就是主线程和当前线程池中的线程传递本地变量的地方,因为这里会新建一个Thread。

详细的多线程源码分析笔者后续会继续准备。。。

  • 31
    点赞
  • 132
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
Java中,ThreadLocal是一种线程局部变量,它可以在多个线程中存储和访问数据,每个线程都有自己独立的副本。然而,ThreadLocal的数据在线程中默认是无法传递的。但是,可以通过一些特殊的ThreadLocal实现类来实现ThreadLocal数据在线程中的传递。 一种实现方式是使用InheritableThreadLocal类。InheritableThreadLocalThreadLocal的一个类,它允许线程继承线程ThreadLocal变量。当一个线程创建线程时,线程会自动拥有线程InheritableThreadLocal变量的副本。这样,线程中设置的ThreadLocal变量的值可以在线程中访问到。 另一种实现方式是使用TransmittableThreadLocal类。TransmittableThreadLocal是一个第三方库,它提供了更强大的功能,可以在线程传递ThreadLocal变量的值。它通过在线程切换时保存和恢复ThreadLocal变量的值来实现传递。使用TransmittableThreadLocal,可以在线程中设置ThreadLocal变量的值,并在线程中访问到这个值。 下面是使用InheritableThreadLocal和TransmittableThreadLocal传递ThreadLocal变量到子线程的示例代码: ```java // 使用InheritableThreadLocal传递ThreadLocal变量到子线程 ThreadLocal<String> threadLocal = new InheritableThreadLocal<>(); threadLocal.set("Hello, World!"); Thread thread = new Thread(() -> { String value = threadLocal.get(); System.out.println("Value in child thread: " + value); }); thread.start(); // 使用TransmittableThreadLocal传递ThreadLocal变量到子线程 ThreadLocal<String> threadLocal = new TransmittableThreadLocal<>(); threadLocal.set("Hello, World!"); Thread thread = new Thread(() -> { String value = threadLocal.get(); System.out.println("Value in child thread: " + value); }); thread.start(); ``` 在上面的代码中,我们首先创建了一个ThreadLocal变量,并在线程中设置了它的值。然后,我们创建了一个线程,并在线程中获取并打印了ThreadLocal变量的值。使用InheritableThreadLocal或TransmittableThreadLocal,我们可以在线程中成功访问到线程设置的ThreadLocal变量的值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值