InheritableThreadLocal父子线程变量共享实现原理
概述
我们知道ThreadLocal 的设计初衷是为了解决多线程并发导致的线程安全问题,向每一个线程提供一个自己的变量副本,实现变量隔离。那如何在不同线程之间共享变量呢?InheritableThreadLocal为解决此问题而生,使用她可以实现父子线程访问ThreadLocal的值。
实现变量传递的关键是在类Thread中定义的本地变量inheritableThreadLocals:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
案例分析
下面先提供一个案例,说明InheritableThreadLocal可以实现父子线程间传递变量。
/**
* 线程间共享变量
*
* @author Wiener
* @date 2020/10/27
*/
public class ShareArgsProblem {
private static void shareArgs() {
//主线程中赋值
ThreadLocal<String> threadLocal = new ThreadLocal<>();
final ThreadLocal itl = new InheritableThreadLocal();
itl.set("帅得一匹!");
threadLocal.set("在 ThreadLocal 中存放了值");
System.out.println(String.format("① 当前线程名称:%s", Thread.currentThread().getName()));
Thread t = new Thread() {
@Override
public void run() {
super.run();
//子线程中读取
System.out.println(String.format("楼兰的胡杨帅么?%s", itl.get()));
System.out.println(String.format("拿不到ThreadLocal 存储的值:%s", threadLocal.get()));
System.out.println(String.format("② 当前线程名称: %s", Thread.currentThread().getName()));
}
};
t.start();
}
public static void main(String[] args) {
shareArgs();
}
}
执行main函数,输出结果如下:

在上面的测试用例中,开启两个线程——主线程和子线程t,主线程中创建了两个变量和一个新的线程t,并尝试在线程t中获取本地变量的值。从输出结果可以看得出来,我们拿不到ThreadLocal类型变量 threadLocal 的值,但是,能够准确拿到 InheritableThreadLocal 类型的变量 itl的值。下面分析为什么可以读取出来InheritableThreadLocal 类型的变量的值。
InheritableThreadLocal共享变量原理分析
先让我们看看InheritableThreadLocal的寥寥数行源码:
/**
* This class extends {@code ThreadLocal} to provide inheritance of values
* from parent thread to child thread: when a child thread is created, the
* child receives initial values for all inheritable thread-local variables
* for which the parent has values. Normally the child's values will be
* identical to the parent's; however, the child's value can be made an
* arbitrary function of the parent's by overriding the {@code childValue}
* method in this class.
*
* <p>Inheritable thread-local variables are used in preference to
* ordinary thread-local variables when the per-thread-attribute being
* maintained in the variable (e.g., User ID, Transaction ID) must be
* automatically transmitted to any child threads that are created.
* @author Josh Bloch and Doug Lea
*/
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {// 值传递
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
类InheritableThreadLocal继承自 ThreadLocal,作为子类,它重写了如上所述的三个方法。从上述注释得知,父线程thread-local 变量会被传递至子线程中,下面看看如何传递的。
public Thread() {//创建新线程时调用的构造方法
this(null, null, "Thread-" + nextThreadNum(), 0);
}
public Thread(ThreadGroup group, Runnable target, String name,
long stackSize) {
this(group, target, name, stackSize, null, true);
}
/**
* Initializes a Thread.
*/
private Thread(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
...
Thread parent = currentThread();
...
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);//①
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
this.tid = nextThreadID();
}
跟着进去几层后,发现在①处给inheritableThreadLocals赋值,继续看 ThreadLocal.createInheritedMap的返回值是什么:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
/**
* Construct a new map including all Inheritable ThreadLocals
* from given parent map. Called only by createInheritedMap.
*
* @param parentMap the map associated with parent thread.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table; //父线程的table
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];//当前线程的table
//循环取父线程的值到当前线程
for (Entry e : parentTable) {
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);//值传递
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
从构造方法ThreadLocalMap可以得知,首先拿到父线程中的键值对表table,然后循环遍历parentTable,把父线程中的这些值通过值传递复制到子线程的ThreadLocalMap对象中,此时子线程的成员变量 inheritableThreadLocals中就有了值。其实,ThreadLocalMap的注释已经明确告诉我们,这里将把父线程的可遗传本地变量(Inheritable ThreadLocals)复制到当前线程中。这种模式,有点类似于ClassLoader的 loadClass() 的机制。
通过查看Object value = key.childValue(e.value)的实现方式,我们知道这里是值传递。
小结
Thread对象通过成员变量ThreadLocal.ThreadLocalMap inheritableThreadLocals维护从父线程(创建该线程的线程)继承而来的数据。原理就是在父线程创建子线程时,如果父线程的inheritableThreadLocals不是null,则ThreadLocalMap中的构造函数会复制一份保存到子线程的inheritableThreadLocals变量中。
由于本地变量在父子传递过程中通过值传递,所以即使父线程的本地变量发生了改变,子线程的本地变量依旧是创建线程时初始化的值。在实际的应用场景里,基本都是使用线程池来进行多线程编程,因线程池复用线程,而非每次创建新的线程,所以如果更新父线程InheritableThreadLocal 的值,被复用的子线程中的InheritableThreadLocal变量值不会被更新,从而导致数据异常。阿里开源的TransmittableThreadLocal 可以解决此问题,感兴趣的老铁们可以去了解一下。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多点赞支持。
Reference
- https://www.cnblogs.com/noteless/p/10448283.html
- https://blog.csdn.net/weixin_34221775/article/details/89657201
- https://it007.blog.csdn.net/article/details/107049975
InheritableThreadLocal父子线程变量共享实现原理的更多相关文章
- 【java】ThreadLocal线程变量的实现原理和使用场景
一.ThreadLocal线程变量的实现原理 1.ThreadLocal核心方法有这个几个 get().set(value).remove() 2.实现原理 ThreadLocal在每个线程都会创建一 ...
- InheritableThreadLocal类原理简介使用 父子线程传递数据详解 多线程中篇(十八)
上一篇文章中对ThreadLocal进行了详尽的介绍,另外还有一个类: InheritableThreadLocal 他是ThreadLocal的子类,那么这个类又有什么作用呢? 测试代码 p ...
- 通过transmittable-thread-local源码理解线程池线程本地变量传递的原理
前提 最近一两个月花了很大的功夫做UCloud服务和中间件迁移到阿里云的工作,没什么空闲时间撸文.想起很早之前写过ThreadLocal的源码分析相关文章,里面提到了ThreadLocal存在一个不能 ...
- InheritableThreadLocal——父线程传递本地变量到子线程的解决方式及分析
转自https://blog.csdn.net/hewenbo111/article/details/80487252 上一个博客提到ThreadLocal变量的基本使用方式,可以看出ThreadLo ...
- 多线程篇四:ThreadLocal实现线程范围内变量共享
1.static实现线程范围内变量共享 package com.test.shareData; import java.util.Random; /** * 多线程范围内的数据共享 * @author ...
- InheritableThreadLocal 在线程池中进行父子线程间消息传递出现消息丢失的解析
在日常研发过程中,我们经常面临着需要在线程内,线程间进行消息传递,比如在修改一些开源组件源码的过程中,需要将外部参数透传到内部,如果进行方法参数重载,则涉及到的改动量过大,这样,我们可以依赖Threa ...
- ThreadLocal父子线程传递实现方案
介绍InheritableThreadLocal之前,假设对 ThreadLocal 已经有了一定的理解,比如基本概念,原理,如果没有,可以参考:ThreadLocal源码分析解密.在讲解之前我们先列 ...
- Java线程:概念与原理
Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程 ...
- Java基础-线程操作共享数据的安全问题
Java基础-线程操作共享数据的安全问题 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.引发线程安全问题 如果有多个线程在同时运行,而这些线程可能会同时运行这段代码.程序每次运 ...
- 【Java并发专题之三】Java线程互斥、协作原理
(I)Java线程互斥原理之synchronized原理 从JDK5引入CAS原子操作,但没有对synchronized关键字做优化,而是增加了J.U.C.concurrent,concurrent包 ...
随机推荐
- LaTeX使用记录
安装与使用 曾在Windows10下装过MikTeX,并配合vscode插件LaTeX Workshop使用过一段时间:这次转到wsl2中,并使用texlive,所以插件的配置json需要小修改 参考 ...
- golang定时器函数 每隔几分钟执行一个函数
延时调用 AfterFunc go function() func function() { // TODO 具体逻辑 // 每5分钟执行一次,递归调用自己 time.AfterFunc(5*time ...
- AAA认证
AAA认证(Authentication, Authorization, and Accounting)是一个网络安全框架,用于管理用户如何访问网络资源.具体来说: 认证(Authentication ...
- 04 详解”死亡ReLU“问题
本篇文章尝试通过具体的神经网络例子,来深入探讨"死亡ReLU"的问题. 很多资料都会提到神经元"永久性死亡"这种说法,我认为这会对我们的理解产生一定的误解.事实 ...
- C#/.NET/.NET Core技术前沿周刊 | 第 31 期(2025年3.17-3.23)
前言 C#/.NET/.NET Core技术前沿周刊,你的每周技术指南针!记录.追踪C#/.NET/.NET Core领域.生态的每周最新.最实用.最有价值的技术文章.社区动态.优质项目和学习资源等. ...
- Lua程序设计笔记
未学:第10章URL编码及以后的示例 13章位和字节 Lua语言基础 一组命令或表达式组成的序列叫chunk程序段,因为Lua语言可以被用作数据定义语言,chunk的大小没有限制,几MB的程序段也很常 ...
- useDeferredValue的作用
前言 useDeferredValue是react18新增的一个用于优化性能的一个hook,它的作用是延迟获取一个值,实际开发中的常规用法与我们之前所用的防抖和节流很相似,但是也有一定的区别.本篇文章 ...
- 轻松的工作(deepseek)
组长:"这里有一百多个地震波形文件,把每一个地震建立一个文件夹,并把地震波形放到对应日期的地震中." 我想:一个一个整好麻烦想摸会鱼 让我们来deepseek吧~ 首先,生成文件夹 ...
- 洛谷P4198 楼房重建 题解
Part1.自己一开始是怎么想的 我一开始的想法是先考虑什么情况下是看不见的. 如果是 \(i < j\) 的话可以直接看 \(j\) 的斜率和 \(i\) 的斜率就是比较 \(\frac{h_ ...
- MySQL隐藏手机号
1.实现方法 通过MySQL的left.right.contact函数实现 2.语法说明 contact()函数 作用:将多个字符串连接成一个字符串 语法:concat(字符串1,....,字符串n) ...