InheritableThreadLocal详解

本文内容:

  1. InheritableThreadLocal可以做什么

  2. InheritableThreadLocal使用实例

  3. InheritableThreadLocal原理

  4. InheritableThreadLocal和线程池搭配使用的问题。

1. InheritableThreadLocal可以做什么

我们知道ThreadLocal解决的是让每个线程读取的ThreadLocal变量是相互独立的。通俗的讲就是,比如我在线程1中set了ThreadLocal的值,那我在线程2中是get不到线程1设置的值的,只能读到线程2自己set的值。

ThreadLocal有一个需求不能满足:就是子线程无法直接复用父线程的ThreadLocal变量里的内容。demo如下:

package com.mt;

public class TestThreadLocal implements Runnable {
    private static ThreadLocal<String> threadLocal = new ThreadLocal<>();

    public static void main(String[] args) {
        System.out.println("----主线程设置值为\"主线程\"");
        threadLocal.set("主线程");
        System.out.println("----主线程设置后获取值:" + threadLocal.get());
        Thread tt = new Thread(new TestThreadLocal());
        tt.start();
        System.out.println("----主线程结束");

    }

    @Override
    public void run() {
        System.out.println("----子线程设置值前获取:" + threadLocal.get());
        System.out.println("----新开线程设置值为\"子线程\"");
        threadLocal.set("子线程");
        System.out.println("----新开的线程设置值后获取:" + threadLocal.get());
    }
}

运行结果:

可以看到虽然在main线程中启动了一个新的子线程,但是threadlocal变量的内容并没有传递到新的子线程中。

于是乎,InheritableThreadLocal就出现了。他可以实现在子线程中使用父线程中的线程本地变量(也即InheritableThreadLocal变量)。

2. InheritableThreadLocal使用实例

demo,根据上面的threadlocal测试代码稍作修改,把Threadlocal换做InheritableThreadLocal。

package com.mt;

public class TestInheritableThreadLocal implements Runnable {
    private static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

    public static void main(String[] args) {
        System.out.println("----主线程设置值为\"主线程\"");
        threadLocal.set("主线程");
        System.out.println("----主线程设置后获取值:" + threadLocal.get());
        Thread tt = new Thread(new TestInheritableThreadLocal());
        tt.start();
        System.out.println("----主线程结束");

    }

    @Override
    public void run() {
        System.out.println("----子线程设置值前获取:" + threadLocal.get());
        System.out.println("----新开线程设置值为\"子线程\"");
        threadLocal.set("子线程");
        System.out.println("----新开的线程设置值后获取:" + threadLocal.get());
    }
}

运行结果如下:

在子线程设置值之前,就已经能够get到主线程设置的值了,说明在父子进制之间传递了InheritableThreadLocal变量。

3.InheritableThreadLocal原理

通过观察InheritableThreadLocal代码Structure,看到只是重写了ThreadLocal的三个方法。
childValue,createMap,getMap。

我们进入到createMap方法中查看。

可以看到,InheritableThreadLocal其实也是用ThreadLocalMap去存放值,这点和ThreadLocal一样,只不过InheritableThreadLocal的变量在Thread类里的名字叫inheritableThreadLocals。我们进到Thread类中看这个变量。

当我们在主线程start一个子线程时,会new 一个Thread。所以我们要追到Thread类中,看看创建线程时发生了什么才让父子线程的InheritableThreadLocal可以传递。

首先我们调用的是Thread(Runnable target)这个方法。

这个方法会调用init方法,然后经过一系列init函数重载,最终来到下面这个init方法。

在这个init方法里 ,跟InheritableThreadLocal紧密相关的有下面这些代码:

重点就是if里面的逻辑。
if (inheritThreadLocals && parent.inheritableThreadLocals != null)

第一项inheritThreadLocals 是传进来的boolean值,重载时传的是true,所以满足条件。

第二项就是判断父线程中的inheritableThreadLocals 是不是空,如果不是空就满足条件。

当同时满足if的两个条件后,就执行
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

新创建出来的子线程的inheritableThreadLocals 变量就和父线程的inheritableThreadLocals 的内容一样了。

以上就是从源码的角度分析InheritableThreadLocal的原理。

4.InheritableThreadLocal和线程池搭配使用的问题

首先给出结论:

InheritableThreadLocal和线程池搭配使用时,可能得不到想要的结果,因为线程池中的线程是复用的,并没有重新初始化线程,InheritableThreadLocal之所以起作用是因为在Thread类中最终会调用init()方法去把InheritableThreadLocal的map复制到子线程中。由于线程池复用了已有线程,所以没有调用init()方法这个过程,也就不能将父线程中的InheritableThreadLocal值传给子线程。

下面是DEMO:

package com.mt;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TestInheritableThreadLocalAndExecutor implements Runnable {
    private static InheritableThreadLocal<String> inheritableThreadLocal = new InheritableThreadLocal<>();
    private static ExecutorService executorService = Executors.newFixedThreadPool(1);

    public static void main(String[] args) throws Exception{
        System.out.println("----主线程启动");
        inheritableThreadLocal.set("主线程第一次赋值");
        System.out.println("----主线程设置后获取值:" + inheritableThreadLocal.get());
        executorService.submit(new TestInheritableThreadLocalAndExecutor());
        System.out.println("主线程休眠2秒");
        Thread.sleep(2000);
        inheritableThreadLocal.set("主线程第二次赋值");
        executorService.submit(new TestInheritableThreadLocalAndExecutor());
        executorService.shutdown();
    }

    @Override
    public void run() {
        System.out.println("----子线程获取值:" + inheritableThreadLocal.get());
    }
}

运行结果:

从上图可以看出,我们在main线程中第二次set并没有被第二次submit的线程get到。也印证了我们的结论。

  • 25
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叹了口丶气

觉得有收获就支持一下吧~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值