【JUC进阶】13. InheritableThreadLocal

这篇具有很好参考价值的文章主要介绍了【JUC进阶】13. InheritableThreadLocal。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1、前言

2、回顾ThreadLocal

3、InheritableThreadLocal

4、实现原理

5、线程池中的问题

6、小结


1、前言

在《【JUC基础】14. ThreadLocal》一文中,介绍了ThreadLocal主要是用于每个线程持有的独立变量。通俗的说就是ThreadLocal是每个线程独有的一份内存,且各个线程间是独立、隔离的。但是随之而来的便会带来如下问题:

  • 如果项目实际场景中,确实需要子线程与父线程共享或复用变量时候,就无法满足。

上面问题的一个解法就是我们今天要介绍的InheritableThreadLocal。

2、回顾ThreadLocal

static ThreadLocal<String> threadLocal = new ThreadLocal<>();

public static void main(String[] args) {
    threadLocal.set("我是主线程的threadlocal变量");
    System.out.println("-----> 主线程" + Thread.currentThread() + " <----- 获取threadlocal变量:" + threadLocal.get());

    new Thread(() -> {
        System.out.println("-----> 子线程" + Thread.currentThread() + " <----- 获取threadlocal变量:" + threadLocal.get());
    }, "son-thread").start();

}

执行结果:

【JUC进阶】13. InheritableThreadLocal,JUC进阶,java,后端

可以看出子线程想要获取父线程的threadlocal变量,是获取不到的。

3、InheritableThreadLocal

前面介绍了背景,那么InheritableThreadLocal是啥呢?他可以做一些啥?从类注释上可以看出InheritableThreadLocal实现了ThreadLocal的扩展,以提供从父线程到子线程的值继承。当创建子线程时,子线程接收父线程有值的所有可继承的线程局部变量的初始值。当在变量中维护每线程属性(例如,User ID)时,优先使用可继承的线程局部变量,而不是普通的线程局部变量。

【JUC进阶】13. InheritableThreadLocal,JUC进阶,java,后端

我们将上面ThreadLocal的demo中,ThreadLocal改为InheritableThreadLocal试下:

static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

public static void main(String[] args) {
    threadLocal.set("我是主线程的threadlocal变量");
    System.out.println("-----> 主线程" + Thread.currentThread() + " <----- 获取threadlocal变量:" + threadLocal.get());

    new Thread(() -> {
        System.out.println("-----> 子线程" + Thread.currentThread() + " <----- 获取threadlocal变量:" + threadLocal.get());
    }, "son-thread").start();

}

执行结果:

【JUC进阶】13. InheritableThreadLocal,JUC进阶,java,后端

可以发现,主线程的变量成功穿透到子线程中。

4、实现原理

结果都看到了,但是我们肯定不能只满足于结果,我们来探究一下他是如何实现的。我们点进去InheritableThreadLocal可以看到,他是ThreadLocal的扩展,且重新实现了childValue(),getMap(),createMap()三个方法。

【JUC进阶】13. InheritableThreadLocal,JUC进阶,java,后端

我们查看createMap()方法,可以看到inheritableThreadLocals变量其实是Thread内部定义的用于线程间共享(inheritable英译:遗传)的变量。

void createMap(Thread t, T firstValue) {
    t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}

Thread.java:

/*
 * InheritableThreadLocal values pertaining to this thread. This map is
 * maintained by the InheritableThreadLocal class.
 */
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;

接着查看inheritableThreadLocals是从哪里赋值的:

【JUC进阶】13. InheritableThreadLocal,JUC进阶,java,后端

重点关注画圈的部分,点进去java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext, boolean),查看代码420行:

...
// 判断inheritThreadLocals为true,我们创建线程new Thread会进入初始化init方法,默认是true
// 且判断parent.inheritableThreadLocals不为空
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
    // 进入该判断,将父线程的inheritableThreadLocals变量赋值给当前线程的inheritableThreadLocals
    this.inheritableThreadLocals =
        ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
...

以上的源码就是InheritableThreadLocal如何实现父子线程变量共享的实现原理了。

5、线程池中的问题

其实不难看出,InheritableThreadLocal只是解决了父子线程共享,或者变量传递的问题。接下来我们改造一下代码,我们通过线程管理多个线程试试看,然后把threadlocal的赋值操作放在创建线程之后:

static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

// 定义线程池,核心线程数为1,方便线程复用
static ExecutorService executorService = Executors.newSingleThreadExecutor();

public static void main(String[] args) throws InterruptedException {

    // 线程池执行子线程
    executorService.submit(() -> {
        System.out.println("-----> 子线程" + Thread.currentThread() + " <----- 获取threadlocal变量:" + threadLocal.get());
    });

    // 主线程睡眠3s,模拟运行
    Thread.sleep(3000);
    // 将变量修改为11111
    threadLocal.set("我是主线程的threadlocal变量,变量值为:11111");

    // 这里线程池重新执行线程任务
    executorService.submit(() -> {
        System.out.println("-----> 子线程" + Thread.currentThread() + " <----- 获取threadlocal变量:" + threadLocal.get());
    });

    // 线程池关闭
    executorService.shutdown();

}

执行结果:

【JUC进阶】13. InheritableThreadLocal,JUC进阶,java,后端

怎么又拿不到了?没错,上面提到InheritableThreadLocal实现值传递主要是根据父线程的map是否有值,再决定要不要赋值给子线程。而父线程的map是通过init一个Thread的时候赋值的。如果我们新创建一个线程,那么肯定会出发创建的初始化方法,必然会进行赋值操作。但是线程池由于线程复用,重复使用的线程在执行异步任务时可能无需再执行创建方法了,因此也就不会再传递父线程的TLMap给子线程了。自然后面获取到的就是null了。

总而言之,就是InheritableThreadLocal进行传递的必须是线程创建的时候赋值的才可以,如果是异步任务中进行赋值的一样是获取不到。如果是线上环境,那么此类问题一般都是偶发的,很容易把你搞脱发。

看到这,我知道你很急,但是你别急。太阳底下无新鲜事,我们不是第一个遇到此类问题的人,别人肯定也遇到过,看看业界是如何实现的。这就是我们接下来要介绍的TransmittableThreadLocal。欲知后事如何,请听下回分解~

6、小结

JUC编程中,往往遇到的问题都不是必现的,具备一定的JUC相关技术基础,可以给你在排障的路上减少一些阻碍。一起学习进步吧。文章来源地址https://www.toymoban.com/news/detail-824816.html

到了这里,关于【JUC进阶】13. InheritableThreadLocal的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【JUC进阶】09. 关于锁升级

    目录 1、前言 2、回顾 2.1、对象头和内存布局 2.2、四大锁回顾 3、状态转换 3.1、锁状态 3.1.1、无锁状态 3.1.2、偏向锁状态 3.1.3、轻量级锁状态 3.1.4、重量级锁状态 3.2、状态转换条件 3.2.1、无锁 - 偏向锁 3.2.2、偏向锁 - 无锁 3.2.3、偏向锁 - 轻量级锁 3.2.4、轻量级锁 - 重量级锁

    2024年02月12日
    浏览(34)
  • 剑指JUC原理-8.Java内存模型

    👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家 📕系列专栏:Spring源码、JUC源码 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦 🍂博主正在努力完成2023计划中:源码溯源,一探究竟 📝联系方式:nhs19990716,加我进群

    2024年02月06日
    浏览(45)
  • Java并发工具合集JUC大爆发!!!

    通常我们所说的并发包也就是java.util.concurrent(JUC),集中了Java并发的各种工具类, 合理地使用它们能帮忙我们快速地完成功能 。 作者: 博学谷狂野架构师 GitHub: GitHub地址 (有我精心准备的130本电子书PDF) 只分享干货、不吹水,让我们一起加油!😄 CountDownLatch是一个同步计

    2023年04月17日
    浏览(76)
  • java JUC并发编程 第六章 CAS

    第一章 java JUC并发编程 Future: link 第二章 java JUC并发编程 多线程锁: link 第三章 java JUC并发编程 中断机制: link 第四章 java JUC并发编程 java内存模型JMM: link 第五章 java JUC并发编程 volatile与JMM: link 第六章 java JUC并发编程 CAS: link 第七章 java JUC并发编程 原子操作类增强: link 第八章

    2024年02月10日
    浏览(46)
  • 【JUC-1】java多线程线程基础知识

    继承Thread类. 实现Runable接口. 实现Callable接口. Runable/Callable接口的实现, 都是重写其中的run/call方法, 实现任务逻辑, 再由线程执行器(可以是Thread类,也可以是线程池)并发执行run/call的逻辑. 而Thread类中的包含start方法, 可以控制线程启动,执行任务. 当发生线程上下文切换时, 操作系

    2024年02月11日
    浏览(39)
  • Java开发 - 不知道算不算详细的JUC详解

    大概有快两周没有发文了,这段时间不断的充实自己,算算时间,也到了该收获的时候,今天带来一篇JUC详解,但说实话,我也不敢自信它详不详细。JUC说白了就是多线程,学Java不久的人知道多线程,恐怕还不知道JUC是什么。在这篇博客中,博主将对JUC做一个自认为比较全面

    2024年02月06日
    浏览(44)
  • Java多线程(3)---锁策略、CAS和JUC

    目录 前言 一.锁策略 1.1乐观锁和悲观锁 ⭐ 两者的概念 ⭐实现方法 1.2读写锁  ⭐概念 ⭐实现方法 1.3重量级锁和轻量级锁 1.4自旋锁和挂起等待锁 ⭐概念 ⭐代码实现 1.5公平锁和非公平锁 1.6可重入锁和不可重入锁 二.CAS 2.1为什么需要CAS 2.2CAS是什么 ⭐CAS的介绍 ⭐CAS工作原理

    2024年02月13日
    浏览(63)
  • 【JavaEE】JUC(Java.util.concurrent)常见类

    经过前面文章的学习我们大致了解了如何实现多线程编程和解决多线程编程中遇到的线程不安全问题, java.util.concurrent 是我们多线程编程的一个常用包,那么今天我将为大家分享 java.util.concurrent 包下的其他几种常见的类。 ReentrantLock 是可重入互斥锁,跟 synchronized 定位是类似

    2024年02月08日
    浏览(38)
  • 【JAVAEE】JUC(java.util.concurrent)的常见类

    目录 1.Callable接口 1.1简介 1.2代码演示  1.3Runnable与Callable的区别 2.ReentrantLock 2.1ReentrantLock的常用方法 2.2ReentrantLock的代码演示 2.3ReentrantLock和synchronized的区别 3.Semaphore信号量 3.1概念 3.2代码演示 4.CountDownLatch 4.1概念 4.2代码演示 JUC是java.util.concurrent包的简称,JDK1.5之后对多线程的

    2024年02月05日
    浏览(38)
  • 【Java多线程】面试常考 —— JUC(java.util.concurrent) 的常见类

    目录 1、JUC(java.util.concurrent) 1.1、Callable 接口 1.2、ReentrantLock 可重入锁 1.3、Semaphore 信号量 1.4、CountDownLatch 这是java中的一个包,存放着 多线程编程 中常见的一些类。 【Java多线程】Thread类的基本用法-CSDN博客 https://blog.csdn.net/zzzzzhxxx/article/details/136121421?spm=1001.2014.3001.5501 往

    2024年04月10日
    浏览(83)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包