源码讲解ThreadLocal父子线程通信问题(图+文+源码)

news2024/9/29 1:18:24

1 缘起

在复习ThreadLocal相关应用的知识,
有一个老生常谈的问题:父子线程通信,
起初,对于父子线程通信,仅了解ThreadLocal无法通过子线程获取线程数据,
并不了解为什么会这样?以及为什么InheritableThreadLocal可以实现父子线程通信?
也是机缘巧合,在先复习的Thread相关属性过程中,发现有两个ThreadLocal.ThreadLocalMap类型的属性:
threadLocals和inheritableThreadLocals,用于存储线程的ThreadLocal相关数据,
结合ThreadLocal和InheritableThreadLocal初始化过程,了解到,分别用户存储数据,
但是,这还不是父子线程通信的根源,接着分析Thread相关,发现,Thread初始化过程,
有通过父线程初始化inheritableThreadLocals属性,定位到根源了,结合ThreadLocal和InheritableThreadLocal的set和get,
最终,全面了解ThreadLocal父子线程通信,
分享如下,帮助读者轻松应对知识交流与考核。

ThreadLocal测试文章

https://blog.csdn.net/Xin_101/article/details/115706182

2 源码分析

2.1 结构

涉及ThreadLocal父子线程通信的核心类有三个:Thread、ThreadLocal和InheritableThreadLocal,
UML关系图如下图所示,InheritableThreadLocal继承ThreadLocal,
ThreadLocal内部类聚合到Thread中,作为属性。
(1)Thread类涉及的方法为init方法,初始化线程,该过程会通过父线程初始化ineritableThreadLocals属性。
(2))ThreadLocal涉及的方法为set、get、createMap,其中,set即存储值到ThreadLocal,get从ThreadLocal获取值,
createMap则是构建ThreadLocalMap,用于存储和获取值;
(3)InheritableThreadLocal继承ThreadLocal,重写了三个方法:childValue、createMap和getMap,其中,
createMap初始化的属性为inheritableThreadLocals,实现从父线程获取inheritableThreadLocals,实现子线程获取父线程数据。
在这里插入图片描述

2.2 存储值:set

ThreadLocal通过set方法将值存储到ThreadLocalMap,
ThreadLocal赋值方法set如下图所示,
由源码可知,先获取当前线程,然后获取线程的ThreadLocalMap,
其中,ThreadLocal获取的ThreadLocalMap为线程的threadLocals属性,
如果获取的ThreadLocalMap为空,则初始化ThreadLocalMap,ThreadLocal初始化的为threadLocals。
位置:java.lang.ThreadLocal#set
在这里插入图片描述
ThreadLocal初始化ThreadLocalMap源码如下图所示,
由源码可知,ThreadLocal初始化的TheadLocalMap为线程的threadLocals属性。
位置:java.lang.ThreadLocal#createMap
在这里插入图片描述
InheritableThreadLocal初始化ThreadLocalMap的源码如下图所示,
由源码可知,InheritableThreadLocal初始化的属性为线程的inheritableThreadLocals,
这为后续从父线程获取inheritableThreadLocals,初始化子线程的inheritableThreadLocals做了准备工作。
java.lang.InheritableThreadLocal#createMap
在这里插入图片描述

2.3 线程初始化:init

根据线程生命周期,可知,线程有一个初始化过程,
线程初始化源码如下图所示,由源码可知,
线程初始化会获取当前线程,即父线程(是个本地方法),
通过父线程获取inheritableThreadLocals,用于初始化子线程的inheritableThreadLocals属性,
初始化子线程的过程为:ThreadLocal.createInheritedMap,
从而,实现父子线程通信,子线程可以获取父线程中存储的数据。
位置:java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext, boolean)

在这里插入图片描述
createInheritedMap源码如下图所示,
由源码可知,createInheritedMap使用父线程的ThreadLocalMap创建新的ThreadLocalMap,
并将该ThreadLocalMap赋给子线程的inheritableThreadLocals,
此时子线程就拥有了父线程的ThreadLcoalMap,继承了该ThreadLocaMap存储的值,
自然可以获取父线程中存储的值。
位置:java.lang.ThreadLocal#createInheritedMap
在这里插入图片描述

2.4 获取值:get

ThreadLoacal通过get方法获取存储在ThreadLocalMap中存储的值,
源码如下图所示,由源码可知,从ThreadLocal获取值,先获取当前线程,
然后获取TheadLocalMap,这里就出现了父子线程通信的分水岭,
(1)ThreadLocal获取TheadLocalMap是从线程的threadLocals属性获取,
而子线程初始化时并没有对自身的threadLocals做任何操作,所以获取的ThreadLocalMap自然是null,
最终返回的值为null;
(2)InheritableThreadLocal获取的ThreadLocalMap是从线程的inheritableThreadLocals属性获取,
而子线程在初始化时,根据父线程inheritableThreadLocals将自身的inheritableThreadLocals同时进行了初始化,
这样,子线程,就从父线程继承了inheritableThreadLocals,获取了相关数据,
在子线程中get数据,自然有值。
这里在补充一下,set过程中,ThreadLocal初始化线程的threadLocals属性,InheritableThreadLocal初始化线程的inheritableThreadLocals属性。
位置:java.lang.ThreadLocal#get
在这里插入图片描述
ThreadLocal获取ThreadLocalMap源码如下图所示,
由源码可知,ThreadLocal从线程的threadLocals属性获取,
而线程初始化过程,并没有通过父线程初始化threadLocals。
位置:java.lang.ThreadLocal#getMap
在这里插入图片描述

InheritableThreadLocal获取ThreadLocalMap源码如下图所示,
由源码可知,InheritableThreadLocal获取ThreadLocalMap从线程的inheritableThreadLocals属性获取,
而,子线程在初始化过程中,会使用父线程的inheritableThreadLocals属性重新初始化子线程的inheritableThreadLocals。
java.lang.InheritableThreadLocal#getMap

在这里插入图片描述

2.5 图解

为方便理解,图解TheadLocal和InheritableThreadLocal父子线程通信问题。

2.5.1 ThreadLocal

TheadLocal父子线程通信图解如下图所示,父线程和子线程间毫无交流,
造成子线程无法获取父线程信息。
在这里插入图片描述

2.5.2 InheritableThreadLocal

TheadLocal父子线程通信图解如下图所示,父线程和子线程通过继承inheritableThreadLocals属性,
实现父子线程通信。
在这里插入图片描述

3 小结

(1)ThreadLocal无法获取父线程数据,ThreadLocal获取数据时直接通过线程的threadLcoals属性获取ThreadLocalMap,由于Thread初始化时没有针对threadLocals进行操作,在子线程中,无法获取父线程的数据;
(2)InheritableThreadLocal可以获取父线程数据,InheritableThreadLocal获取数据时通过线程的inheritableThreadLcoals属性获取ThreadLocalMap,由于Thread初始化时通过父线程初始化了inheritableThreadLocals,所以在子线程中,可以正常获取父线程的数据;
(3)核心在于Thread的初始化过程,java.lang.Thread#init(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String, long, java.security.AccessControlContext, boolean)方法初始化时针对inheritableThreadLocals做了初始化,解决了父子线程通信问题;
(4)ThreadLocal在获取值过程中,首先要获取当前线程,也是导致无法父子通信的一个原因,虽然保证了数据的隔离性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/139346.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

15.4 宏任务和微任务

宏任务和微任务 start 如果彻底理解了事件循环,其实大多数 JS 执行的逻辑都能理解了但是在 ES6 中引入了 Promise, 就引出了两个新概念,宏任务和微任务。 1.宏任务和微任务 1.1 名词 宏任务:macrotask 微任务:microtask 在 E…

异构操作系统的“融合计算”

这些年,由随着应用场景日益丰富和多样化,计算工作越来越复杂,传统的计算方式(单机计算/分布式计算)已经不能满足,需要一种新的更强大的计算模式来解决这些问题,这是融合计算产生的背景。 …

117.(leaflet之家)leaflet空间判断-点与geojson面图层的空间关系(turf实现)

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <!DOCTYPE html> <html>

Linux系统下的服务管理

文章目录Linux系统下的服务管理1.基本介绍2.service管理指令3.chkconfig指令4.systemctl管理指今4.1.基本语法4.2.systemctl设置服务的自启动状态4.3.防火墙实验案例4.4.防火墙Linux系统下的服务管理 1.基本介绍 服务(service) 本质就是进程&#xff0c;但是是运行在后台的&a…

软考高项(信息系统项目管理师)经验分享

文章目录前言考试过程第一步&#xff1a;日常刷选择题第二步&#xff1a;考前一个月之前刷完精讲课第三步&#xff1a;计算题统一学习第四步&#xff1a;论文早准备第五步&#xff1a;反复刷冲刺视频第六步&#xff1a;刷近几年真题第七步&#xff1a;准备几份考试当天复习资料…

vue组件之间的数据传递和组件的生命周期

一、组件之间的通信1、组件之间的关系&#xff1a;父子关系、兄弟关系、跨级关系2、父子组件之间的通信&#xff08;数据传递&#xff09;&#xff1a;&#xff08;1&#xff09;父组件 ——-> 子组件&#xff1a;使用propsA、第一步&#xff1a;在父组件中使用子组件时&…

【K哥爬虫普法】大数据风控第一案:从魔蝎科技案件判决,看爬虫技术刑事边界

我国目前并未出台专门针对网络爬虫技术的法律规范&#xff0c;但在司法实践中&#xff0c;相关判决已屡见不鲜&#xff0c;K 哥特设了“K哥爬虫普法”专栏&#xff0c;本栏目通过对真实案例的分析&#xff0c;旨在提高广大爬虫工程师的法律意识&#xff0c;知晓如何合法合规利用…

线段树 - 从入门到入土

普通线段树 线段树是什么 我们要学习线段树&#xff0c;首先要了解线段树的结构长什么样。 线段树是一颗二叉树&#xff0c;树上的节点储存数据&#xff08;可以是值、字符串、数组、多个值&#xff09;。 作用 一般来说&#xff0c;线段树是用来维护一个数组的。 数据储…

手写RPC框架02-路由模块设计与实现

源码地址&#xff1a;https://github.com/lhj502819/IRpc/tree/v3 系列文章&#xff1a; 注册中心模块实现路由模块实现序列化模块实现过滤器模块实现 为什么需要路由模块&#xff1f; 在当今互联网日益发展的情况下&#xff0c;我们一个服务一般都会部署多个&#xff0c;一方…

Python绘制表白代码,又是一个表白神器

前言 嗨呀&#xff0c;又是我&#xff0c;又给你们带来了表白的代码 之前发了那些 照片里面加文字的…还有烟花…还有跳动爱心…emm你们也可以去看看哦 今天带来的这个&#xff0c;也是很不错哦 只不过它出来的有些慢&#xff0c;我这里先给你们看看这个效果图吧 效果展示…

大数据基础平台搭建-(三)Hadoop集群HA+Zookeeper搭建

大数据基础平台搭建-&#xff08;三&#xff09;Hadoop集群HAZookeeper搭建 大数据平台系列文章&#xff1a; 1、大数据基础平台搭建-&#xff08;一&#xff09;基础环境准备 2、大数据基础平台搭建-&#xff08;二&#xff09;Hadoop集群搭建 3、大数据基础平台搭建-&#xf…

如何让小型云台机械手实现按颜色分拣物品?

1. 功能说明 在小型云台机械手附近设置一个工作台&#xff0c;并安装一个TCS3200颜色识别传感器。将红色、蓝色工件分别放置在传感器上&#xff0c;如果检测的物料的颜色为红色&#xff0c;机械臂将物体放在机械臂的左侧&#xff0c;如果检测的物料的颜色为蓝色&#xff0c;机械…

数据结构与算法-希尔排序、归并排序

目录​​​​​​​ 希尔排序 1.算法描述 2.算法的实现 归并排序 4.1算法描述 2.算法实现 希尔排序 1.算法描述 1959年shell发明&#xff0c;第一批突破O&#xff08;n2&#xff09;时间复杂度的排序算法&#xff0c;是简单插入排序的改进版。它与插入之处在于&#xff0…

Android 深入系统完全讲解(二)

操作系统 操作系统是一套软件&#xff0c;它的任务就是为上层开发的用户&#xff0c;提供一个更方便的开发环境&#xff0c;同时 让硬件连接到系统中&#xff0c;能够非常方便&#xff0c;从而提高开发速度&#xff0c;以及稳定可靠。 操作系统就是这么存在的。 我们理解它&am…

基于低代码平台构筑金融行业IT运维服务体系

我今天分享题目是《基于低代码平台&#xff0c;构筑金融行业的IT运维服务体系》。这是一个大家不太熟悉的领域&#xff0c;首先它的行业是金融&#xff0c;其次它做的事情是IT运维。 关于金科信息 我先介绍一下金科信息。金科信息在1993年成立&#xff0c;到明年我们就整整30…

前端开发技术栈(插件篇):datatables

一、Datatables介绍 官网地址&#xff1a;https://datatables.net/ DataTables中文网&#xff1a;http://datatables.club/ 1、Datatables是一款jquery表格插件。它是一个高度灵活的工具&#xff0c;可以将任何HTML表格添加高级的交互功能。 2、分页&#xff0c;即时搜索和排序…

【Bootstrap】可复用的组件

目录 一、字体图标 二、下拉菜单 1. 步骤 2. 对齐 3. 分割线 4. 禁用的菜单项 三、按钮组 1. 按钮组 2. 按钮工具栏 3. 尺寸 4. 嵌套 5. 垂直排列 四、输入框组 1. 输入框组 2. 尺寸 3. 作为额外元素的按钮 4. 作为额外元素的按钮式下拉菜单 一、字体图标 组件…

55、MySOL数据库

目录 一、MySQL安装和配置 二、数据库 三、表 四、数据库的C [create] R [read] U [update] D [delete] 语句 1、insert语句 2、update语句 3、delete语句 4、select语句 五、Mysql常用数据类型&#xff08;列类型&#xff09;&#xff1a;​编辑 六、函数 *合计 / 统计…

kettle - 清洗 mongodb 数据案例

文章目录前言kettle - 清洗 mongodb 数据案例一、需求二、kettle开发1、新建mongodb数据查询2、配置kettleTest集合与清洗后kettleTestClear集合字段映射3、根据_id进行排序4、使用java脚本将日期格式化5、进行字段选择6、将delete字段进行值映射7、mongo输出8、最后加一个写日…

详解哨兵之间是如何通信的

基于 pub/sub 机制的哨兵集群组成 哨兵实例之间可以相互发现&#xff0c;要归功于 Redis 提供的 pub/sub 机制&#xff0c;也就是发布 / 订阅机制。 哨兵只要和主库建立起了连接&#xff0c;就可以在主库上发布消息了&#xff0c;比如说发布它自己的连接信息&#xff08;IP 和…