子线程无法访问父线程中通过ThreadLocal设置的变量

一、引出结论

学习过ThreadLocal的童鞋都知道,在子线程中,是无法访问父线程通过ThreadLocal设置的变量的。

package thread;/*** @author heyunlin* @version 1.0*/
public class ThreadLocalExample {public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {ThreadLocal<String> threadLocal = new ThreadLocal<>();threadLocal.set("hello");Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("run()...");/** 子线程中无法访问父线程中设置的ThreadLocal变量*/System.out.println(threadLocal.get());}});thread.start();thread.join();System.out.println(thread);System.out.println(threadLocal.get());}}

运行结果:

InheritableThreadLocal就是为了解决这个不可见问题而生的~

package thread;/*** @author heyunlin* @version 1.0*/
public class InheritableThreadLocalExample {public static void main(String[] args) throws InterruptedException, NoSuchFieldException, IllegalAccessException {InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();threadLocal.set("hello");Thread thread = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("run()...");System.out.println(threadLocal.get());}});thread.start();thread.join();System.out.println(thread);System.out.println(threadLocal.get());}}

运行结果:

二、分析原因

那么, 究竟是什么原因导致的子线程无法访问父线程中的ThreadLocal变量呢,接下来研究ThreadLocal这个类的源码。

先看一下这个ThreadLocal类上的文档注释,大概了解ThreadLocal是用来干什么的

/*** This class provides thread-local variables.  These variables differ from* their normal counterparts in that each thread that accesses one (via its* {@code get} or {@code set} method) has its own, independently initialized* copy of the variable.  {@code ThreadLocal} instances are typically private* static fields in classes that wish to associate state with a thread (e.g.,* a user ID or Transaction ID).** <p>For example, the class below generates unique identifiers local to each* thread.* A thread's id is assigned the first time it invokes {@code ThreadId.get()}* and remains unchanged on subsequent calls.* <pre>* import java.util.concurrent.atomic.AtomicInteger;** public class ThreadId {*     // Atomic integer containing the next thread ID to be assigned*     private static final AtomicInteger nextId = new AtomicInteger(0);**     // Thread local variable containing each thread's ID*     private static final ThreadLocal&lt;Integer&gt; threadId =*         new ThreadLocal&lt;Integer&gt;() {*             &#64;Override protected Integer initialValue() {*                 return nextId.getAndIncrement();*         }*     };**     // Returns the current thread's unique ID, assigning it if necessary*     public static int get() {*         return threadId.get();*     }* }* </pre>* <p>Each thread holds an implicit reference to its copy of a thread-local* variable as long as the thread is alive and the {@code ThreadLocal}* instance is accessible; after a thread goes away, all of its copies of* thread-local instances are subject to garbage collection (unless other* references to these copies exist).** @author  Josh Bloch and Doug Lea* @since   1.2*/

1、关键注释

博主在类的文档注释上提取了几个关于ThreadLocal的重要的说明

This class provides thread-local variables. 

这个类提供了线程本地变量

ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread. 

ThreadLocal实例提供了可以关联一个线程的静态字段。

Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).

只要线程是存活状态,并且ThreadLocal对象可以访问,每个线程都拥有一个线程本地变量的副本的引用;

在线程执行完方法之后,所有线程本地变量都会被gc(垃圾收集器)回收,除非有其他变量引用了它。

2、研究源码

经过上面博主的大致翻译,已经对ThreadLocal有了一定的了解,接下来深入源码去学习ThreadLocal的工作原理。

在不了解ThreadLocal的情况下,直接从我们直接调用的get()和set()方法入手。

public class ThreadLocal<T> {public T get() {Thread thread = Thread.currentThread();ThreadLocalMap map = getMap(thread);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {T result = (T)e.value;return result;}}return setInitialValue();}public void set(T value) {Thread thread = Thread.currentThread();ThreadLocalMap map = getMap(thread);if (map != null) {map.set(this, value);} else {createMap(t, value);}}}

set()方法

    public void set(T value) {// 获取当前线程Thread thread = Thread.currentThread();// 根据当前线程获取ThreadLocalMap对象ThreadLocalMap map = getMap(thread);// map不为null,设置初始值到map中if (map != null) {map.set(this, value);} else {// map是null,创建一个mapcreateMap(t, value);}}

 getMap()

看样子和线程类Thead里的一个threadLocals变量有关。

    ThreadLocal.ThreadLocalMap getMap(Thread thread) {// 这个方法就是返回Thread的threadLocals变量的值return thread.threadLocals;}

ThreadLocalMap是ThreadLocal的一个静态内部类

public class ThreadLocal<T> {static class ThreadLocalMap {}}

 

createMap()
    void createMap(Thread thread, T firstValue) {// 初始化线程的threadLocals变量thread.threadLocals = new ThreadLocal.ThreadLocalMap(this, firstValue);}

set()方法总结

根据set()方法的源代码,set()方法是把值设置到Thead线程类的threadLocals变量中,这个变量应该是一个Map派生类的实例。

get()方法

    public T get() {Thread thread = Thread.currentThread(); // 获取当前线程// 通过getMap()方法获取一个ThreadLocalMap对象// 因为这个类是以Map结尾的,推测是java.util.Map的派生类ThreadLocalMap map = getMap(thread);// 获取到的map不为nullif (map != null) {// 获取Map的Entry// 说明我们的推测大概率是正确的,因为HashMap中也有个Enty,而且也有value()方法ThreadLocalMap.Entry e = map.getEntry(this);// 获取的value不为空,则转为指定的类型T(泛型)直接返回if (e != null) {T result = (T) e.value;return result;}}// 代码运行到这里,说明获取到的map是null// 因为前面的if中已经通过return结束方法return setInitialValue();}

getMap()

这个方法的代码在前面set()方法里已经看了~

createMap()

这个方法的代码在前面set()方法里已经看了~

setInitialValue()

这个方法和set()方法的代码几乎一模一样。知识多了一个返回值~

T value = initialValue();

set()方法的代码

return value;

    private T setInitialValue() {// 通过initialValue()方法获取一个初始化值T value = initialValue();// 获取当前线程Thread thread = Thread.currentThread();// 根据当前线程获取ThreadLocalMap对象ThreadLocalMap map = getMap(thread);// map不为null,设置初始值到map中if (map != null) {map.set(this, value);} else {// map是null,创建一个mapcreateMap(thread, value);}// 返回初始值return value;}

set()

ThreadLocal.ThreadLocalMap的set()方法,有点复杂,类似HashMap的底层源码实现,跳过~

    private void set(ThreadLocal<?> key, Object value) {ThreadLocal.ThreadLocalMap.Entry[] tab = table;int len = tab.length;int i = key.threadLocalHashCode & (len-1);for (ThreadLocal.ThreadLocalMap.Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {ThreadLocal<?> k = e.get();if (k == key) {e.value = value;return;}if (k == null) {replaceStaleEntry(key, value, i);return;}}tab[i] = new ThreadLocal.ThreadLocalMap.Entry(key, value);int sz = ++size;if (!cleanSomeSlots(i, sz) && sz >= threshold) {rehash();}}

get()方法总结

综合上面关于get()方法的源代码,get()方法是从Thead线程类的threadLocals变量中获取数据。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

毕设 大数据校园卡数据分析

文章目录 0 前言1 课题介绍2 数据预处理2.1 数据清洗2.2 数据规约 3 模型建立和分析3.1 不同专业、性别的学生与消费能力的关系3.2 消费时间的特征分析 4 Web系统效果展示5 最后 0 前言 &#x1f525; 这两年开始毕业设计和毕业答辩的要求和难度不断提升&#xff0c;传统的毕设…

脑机接口习题

9-12章习题 填空题 EEG电极分为 主动电极 和 被动电极 &#xff0c;其中 被动电极 直接与放大器连接&#xff0c; 主动电极 包含一个1~10倍的前置放大。除抗混淆滤波器&#xff0c;放大系统也包含由电阻器、电容器构成的模拟滤波器&#xff0c;把信号频率内容限制在一个特定的…

【QGIS入门实战精品教程】10.7: 基于DEM的地形因子分析(坡度、坡向、粗糙度、山体阴影、耐用指数)

文章目录 一、加载dem二、山体阴影三、坡度四、坡向五、地形耐用指数六、地形位置指数七、地表粗糙度一、加载dem 二、山体阴影 方法一:符号系统 利用符号系统中的山体阴影,渲染出阴影效果。 方法二:山体阴影工具 该算法计算输入中的数字化地形模型的山体阴影。根据太阳的位…

MFC密码对话框之间数据传送实例(源码下载)

新建一个login工程项目对话框&#xff0c;主对话框IDD_LOGIN_DIALOG中一个显示按钮IDC_BUTTON1、一个密码按钮IDC_BUTTON2。添加一个密码对话框IDD_DIALOG1&#xff0c;添加类password&#xff0c;在对话框中添加一个编辑框IDC_EDIT1、一个确定按钮IDC_BUTTON1。 程序功能&…

Flask和Django的对比

文章目录 1. 简介FlaskDjango 2. 安装和创建项目FlaskDjango 3. URL路由FlaskDjango 4. 数据库支持FlaskDjango 5. 管理后台FlaskDjango 6. 总结 Flask和Django都是Python Web框架&#xff0c;它们在开发Web应用程序时都能提供强大的功能。本文将对这两个框架进行对比&#xff…

2024目前网上最火短剧机器人做法,自动搜索发剧 自动更新资源 自动分享资源

目前整个项目圈子很多的短剧机器人&#xff0c;我写的&#xff0c;自动搜索发剧&#xff0c;自动更新资源&#xff0c;自动分享资源&#xff0c;前段时间大部分做短剧的都是做的短剧分成&#xff0c;我的一个学员做的30W播放量才200块收益&#xff0c;备受启发&#xff0c;我就…

VUE3视频播放器 videojs-player/vue

简介 官网&#xff1a; https://gitcode.com/surmon-china/videojs-player/overviewhttps://github.com/surmon-china/videojs-player?tabreadme-ov-file video-player是一个基于video.js的视频播放器组件&#xff0c;它提供了丰富的功能&#xff0c;包括视频播放、暂停、快…

MySQL——优化

全文搜索最慢 EXPLAIN select * from city; 范围搜索 EXPLAIN select * from city where ID>5 and ID<20; 主键查询 EXPLAIN select * from citywhere ID5; 索引查询 EXPLAIN select * from citywhere CountryCodeNLD; 普通查询 EXPLAIN select * from city where Nam…

【yolov10】使用自己的数据集训练目标检测模型

【yolov10】使用自己的数据集训练目标检测模型 一、anaconda安装二、环境配置三、数据集制作1、labelimg的安装2、使用labelimg 四、正片1、下载yolov10源码2、数据集目录3、训练4、推理 一、anaconda安装 直接参考前一篇博客&#xff1a; https://blog.csdn.net/m0_71523511/…

【计算机毕业设计】基于SSM+Vue的新能源汽车在线租赁管理系统【源码+lw+部署文档】

摘 要 随着科学技术的飞速发展&#xff0c;社会的方方面面、各行各业都在努力与现代的先进技术接轨&#xff0c;通过科技手段来提高自身的优势&#xff0c;新能源汽车在线租赁当然也不能排除在外。新能源汽车在线租赁是以实际运用为开发背景&#xff0c;运用软件工程开发方法&…

文本处理工具grep及sed

文章目录 一、grep文本处理工具二、sed文本处理工具基本用法sed脚本格式搜索替代 一、grep文本处理工具 选项含义-color对匹配到的文本着色显示-m 次数匹配到规定的次数后停止-v显示不被命令匹配到的行,即取反-i忽略字符大小写-n显示匹配的行号-c统计匹配的行数-o仅显示匹配到…

aosp14的分屏接口ISplitScreen接口获取方式更新-学员疑问答疑

背景&#xff1a; 有学员朋友在学习马哥的分屏pip自由窗口专题时候&#xff0c;做相关分屏做小桌面项目时候&#xff0c;因为原来课程版本是基于android 13进行的讲解的&#xff0c;但是现在公司已经开始逐渐进行相关的android 14的适配了&#xff0c;但是android 14这块相比a…

Linux服务升级:Predixy 升级代理 Redis-cluster 集群

目录 一、实验 1.环境 2. 启动Redis服务 3.Predixy 升级代理 Redis-cluster 集群 二、问题 1. Predixy进行set操作报错 2.如何创建脚本启动predixy 3.Redis代理对比 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统版本节点软件IP备注CentOS7.9Redis…

嵌入式全栈开发学习笔记---C语言笔试复习大全22

目录 结构体 结构体的声明 定义结构体变量 访问结构体成员进行初始化 通过结构体变量名访问结构体成员 结构体指针 结构体指针的定义 通过结构体指针访问结构体成员 结构体数组 结构体数组的定义 遍历结构体数组 结构体的长度&#xff08;笔试重点&#xff09; 上一…

就业班 第三阶段(ELK) 2401--5.20 day1 ELK 企业实战 ES+head+kibana+logstash部署(最大集群)

ELKkafkafilebeat企业内部日志分析系统 1、组件介绍 1、Elasticsearch&#xff1a; 是一个基于Lucene的搜索服务器。提供搜集、分析、存储数据三大功能。它提供了一个分布式多用户能力的全文搜索引擎&#xff0c;基于RESTful web接口。Elasticsearch是用Java开发的&#xff…

上门服务系统开发|东邻到家系统|上门服务系统开发流程

上门服务小程序的开发流程是一个复杂且精细的过程&#xff0c;涉及到需求分析、设计规划、开发实施、测试验收以及上线运营等多个环节。下面将详细介绍上门服务小程序的开发流程&#xff0c;帮助读者全面了解并掌握其中的关键步骤。 一、需求分析 在开发上门服务小程序之前&am…

2024年全国大学生电工数学建模竞赛B题解析 | 数据处理 代码 论文分享

B 题&#xff1a;大学生平衡膳食食谱的优化设计及评价 1 数据预处理2 问题一2.1 问题1.12.1.1 评价体系的构建2.1.2 指标计算2.1.3 指标计算结果2.1.4 基于层次分析法的膳食营养评价模型2.1.5 评价模型的求解 2.2 问题1.22.2.1 食物与成分间拓扑关系的构建2.2.2 微调模型的建立…

原哥花了1个多月的时间终于开发了一款基于android studio的原生商城app

大概讲一下这个app实现的功能和前后端技术架构。 功能简介 广告展示商品展示跳转淘宝联盟优惠卷购买发布朋友圈宝妈知识资讯商品搜索朋友圈展示/点赞/评论登陆注册版本升级我的个人资料商品和资讯收藏我的朋友圈意见反馈 安卓端技术选型 Arouter组件化daggerrxjavaretrofit…

音视频开发4-补充 FFmpeg 开发环境搭建 -- 在windows 上重新build ffmpeg

本节的目的是在windows 上 编译 ffmpeg 源码&#xff0c;这样做的目的是&#xff1a;在工作中可以根据工作的实际内容裁剪 ffmpeg&#xff0c;或者改动 ffmpeg 的源码。 第一步 &#xff1a;下载&#xff0c; 安装&#xff0c;配置 &#xff0c;运行 msys64 下载 下载地址&…

嵌入式进阶——LED呼吸灯(PWM)

&#x1f3ac; 秋野酱&#xff1a;《个人主页》 &#x1f525; 个人专栏:《Java专栏》《Python专栏》 ⛺️心若有所向往,何惧道阻且长 文章目录 PWM基础概念STC8H芯片PWMA应用PWM配置详解占空比 PWM基础概念 PWM全称是脉宽调制&#xff08;Pulse Width Modulation&#xff09…