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包 ...
随机推荐
- Avalanche公链深度解析:创新共识、亚秒级最终性与生态竞争力
摘要:Avalanche定位为一个高性能.可扩展的Layer 1区块链平台,但它并不是一个新公链,其主网于2020年9月21日正式上线,有Ava Labs开发.Ava Labs成立于2018年,总部位 ...
- python正则表达式笔记2
由 '\' 和一个字符组成的特殊序列在以下列出. 如果普通字符不是ASCII数位或者ASCII字母,那么正则样式将匹配第二个字符.比如,\$ 匹配字符 '$'. \number匹配数字代表的组合.每个 ...
- vue学习二(过滤器)
过滤器常用户来处理文本格式化的操作 过滤器还可以用在两个地方:花括号和v-bind 表达式 1.全局过滤器 {{user.gender|gfilter}} Vue.filter("gfil ...
- pandas(进阶操作)-- 政治献金项目数据分析
博客地址:https://www.cnblogs.com/zylyehuo/ 开发环境 anaconda 集成环境:集成好了数据分析和机器学习中所需要的全部环境 安装目录不可以有中文和特殊符号 jup ...
- GPU CPU运算时间测试
GPU CPU运算时间测试 本文主要探讨GPU,CPU在做一些复杂运算的时间测试 实验任务 1.向量加法 两个相同维度的向量a,b做加法,分别测试GPU并行时间(包含数据拷贝时间),CPU串行时间. ...
- Netty源码—7.ByteBuf原理二
大纲 9.Netty的内存规格 10.缓存数据结构 11.命中缓存的分配流程 12.Netty里有关内存分配的重要概念 13.Page级别的内存分配 14.SubPage级别的内存分配 15.Byte ...
- 【C语言】解决初始化数组时报错“undefined reference to `memcpy'”
[C语言]解决初始化数组时报错"undefined reference to `memcpy'" 零.报错 代码: char start[] = {0xd, 0xa, 0xb3, ...
- sql数据库连接
前言 作为数据存储的数据库,近年来发展飞快,在早期的程序自我存储到后面的独立出中间件服务,再到集群,不可谓不快了.早期的sql数据库一枝独秀,到后面的Nosql,还有azure安全,五花八门,教人学不 ...
- sorting order 和sorting layer
根据unity的api文档 https://docs.unity3d.com/ScriptReference/Renderer-sortingOrder.html Renderer's order w ...
- 使用Python可视化偶极子的电场
引言 在电学中,偶极子是一个非常重要且有趣的概念.它由两个电荷(一个正电荷和一个负电荷)组成,并且这两个电荷具有相同的大小和相反的符号.偶极子的电场分布具有独特的特点,能够帮助我们深入理解电场的性质. ...