java 浅谈ThreadLocal底层源码(通俗易懂)

目录

一、ThreadLocal类基本介绍

        1.概述 : 

        2.作用及特定 : 

二、ThreadLocal类源码解读

        1.代码准备 : 

            1.1 图示 

            1.2 数据对象

            1.3 测试类

            1.4 运行测试

        2.源码分析 : 

            2.1 set方法解读

            2.2 get方法解读


一、ThreadLocal类基本介绍

        1.概述 : 

        (1) ThreadLocal,本地线程变量,是Java中的一个类。ThreadLocal类提供了一种线程绑定机制,可以将状态与线程(Thread)关联起来。ThreadLocal类如下图所示 : 

        (2) 每个线程都会有自己独立的一个ThreadLocal变量,该变量对其他线程而言是封闭且隔离的,因此对该变量的读写操作只会影响到当前执行线程的这个变量,而不会影响到其他线程的同名变量

        2.作用及特定 : 

        (1) ThreadLocal可以实现在同一个线程数据共享,从而解决多线程数据安全问题。

        (2) 通过ThreadLocal类的set方法,可以未当前线程关联一个数据(变量,对象,数组)

        (3) 通过ThreadLocal类的get方法,可以像Map一样存取key-value键值对(其中key为当前线程),注意,显式的用法上与Map不相同

        (4) 每一个ThreadLocal对象,只能为当前线程关联一个数据,若想为当前线程关联多个数据,就需要使用到多个ThreadLocal实例

        (5) ThreadLocal实例往往定义为static类型。

        (6) ThreadLocal中保存的数据,会在线程销毁后自动释放


二、ThreadLocal类源码解读

        1.代码准备 : 

            1.1 图示 

                首先,我们要把代码打通,确保ThreadLocal对象可以在同一线程中实现数据共享。根据下图来定义所需要的测试类 : 

                在T1类,T1Service类,以及T2DAO类中分别打印出当前线程的名字,以及放入到threadLocal1对象中的数据对象,对比三个类打印出的线程名字和数据对象是否相同,即可验证“ThreadLocal可以实现在同一个线程数据共享”。

            1.2 数据对象

                定义Apple类和Grape类用作测试的数据对象。
                Apple类代码如下 : 

package threadlocal;public class Apple {
}

                Grape类代码如下 : 

package threadlocal;/*** @author : Cyan_RA9* @version : 21.0*/
public class Grape {
}

            1.3 测试类

                T1类代码如下 : 

package threadlocal;/*** @author : Cyan_RA9* @version : 21.0*/
public class T1 {//定义一个静态的ThreadLocal对象public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();public static void main(String[] args) {//在主线程中启动一个新的子线程new Thread(new Task()).start();}public static class Task implements Runnable{@Overridepublic void run() {System.out.println("(Task)run方法,当前线程名 = " + Thread.currentThread().getName());Apple apple = new Apple();Grape grape = new Grape();//向threadLocal1对象中放入一个Apple对象System.out.println("(Task)run方法,放入的对象 = " + apple);threadLocal1.set(apple);new T1Service().test();}}
}

                T1Service类代码如下 : 

package threadlocal;/*** @author : Cyan_RA9* @version : 21.0*/
public class T1Service {public void test() {String name = Thread.currentThread().getName();System.out.println("(T1Service)当前线程名 =  " + name);Object o = T1.threadLocal1.get();System.out.println("(T1Service)得到的对象o = " + o);new T2DAO().test();}
}

                T2DAO类代码如下 : 

package threadlocal;/*** @author : Cyan_RA9* @version : 21.0*/
public class T2DAO {public void test() {String name = Thread.currentThread().getName();System.out.println("(T2DAO)当前线程名 = " + name);Object o = T1.threadLocal1.get();System.out.println("(T2DAO)得到的对象o = " + o);}
}

            1.4 运行测试

                运行结果 : 

        2.源码分析 : 

            2.1 set方法解读

                set方法源码如下 : 

    public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}}

                第一步,可以看到,set方法中首先就获取到了当前线程,而当前线程,就是调用set方法时——线程类run方法所在的那个线程;说明set方法和当前线程是关联的
                第二步通过当前线程对象获取到了ThreadLocalMap对象。此处的ThreadLocalMap,是ThreadLocal类的一个静态内部类。如下图所示 : 

                注意,为什么是通过当前线程对象来获取ThreadLocalMap对象呢?
                因为当前线程持有自己的ThreadLocal对象(该对象调用了set方法),而ThreadLocalMap又是ThreadLocal的一个内部类.
                
继续,接着判断得到的map对象是否为空,如下图所示 : 

                如果不为空,就将当前的ThreadLocal对象(this,即指在T1类中一开始调用set方法的ThreadLocal对象)和该对象调用set方法时放入的数据(value,此处是放入的Apple对象)。从这里也可以看出,如果同一个ThreadLocal对象再次调用set方法,会对存入的数据(value)进行替换——即"每一个ThreadLocal对象,只能为当前线程关联一个数据"。
                如果为空,就创建一个与当前线程对象关联的ThreadLocalMap对象,并将目标数据放入(value)。

                在set方法调用处设一个断点,进入Debug界面后可以看到当前线程对象名字,如下图所示 : 

                在this对象中向下找,可以找到一个threadLocals属性,发现它就是ThreadLocalMap类型,如下图所示 : 

                我们也可以Thread类的源码中找到这个属性,如下图所示 :

                而该属性下的table, 就是实际存放数据的地方(可以看到table是ThreadLocalMap内部类的内部类Entry类型的数组)。当set方法执行完毕后,我们可以看到table数组中的Apple对象,如下图所示 :

                实际上,table就是线程用于管理ThreadLocal实例的容器
                而table数组中每个元素的referent属性(弱引用对象),也就是ThreadLocal对象,此处可以看到与之前一致,如下 : 

            2.2 get方法解读

                get方法源码如下 : 

    public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}

                第一步,和set方法一样,都是先得到当前的线程对象。为啥?因为只有得到了当前线程对象,才能找到它的属性threadLocals[ThreadLocal$ThreadLocalMap类型],继而找到该属性维护的table数组(ThreadLocal$ThreadLocalMap$Entry[]类型),然后根据当前线程持有的的ThreadLocal实例,找到数组中对应的Entry元素,继而拿到它的属性value(保存的数据)
                显然,get方法的源码中也的确是这么干的。通过当前线程对象拿到ThreadLocalMap对象,我们可以看一下getMap(t)的源码,如下图所示 :

                之后,判断map对象是否为空,如果不为空,就根据当前持有的ThreadLocal实例去找table数组中对应的Entry元素。继续往下走 : 

                拿到对应的Entry元素后,还要进行判断,如果该元素的确是存在的(表明当前的ThreadLocal实例已经为当前线程绑定过数据[一个value]), 就取出该Entry元素的value属性,此处为Object类型的apple对象,然后返回。

                🆗,以上就是对ThreadLocal的一些浅显解读。感谢阅读!

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

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

相关文章

python3.11教程1:python基础语法、程序控制、函数

文章目录 一、Python简介1.1 为什么学习python1.2 python安装与配置1.3 python解释器1.4 命令行参数1.4.1 sys.argv变量1.4.2 -c和-m选项 1.5 解释器的运行环境1.5.1 编码格式1.5.2 编码声明 二、Python基础语法2.1 行结构2.2 变量&#xff08;标识符&#xff09;2.3 字节串2.4…

神经网络NLP基础 循环神经网络 LSTM

用的时候&#xff0c;只关心token的输入&#xff0c;以及hidden state就好了 sequence的length是多少&#xff0c;lstm的cell的数量就是多少 LSTM BI-LSTM stacked lstm GRU 实现

【计算机网络】TCP 的三次握手和四次挥手

TCP 是面向连接的&#xff0c;面向连接就是数据通讯的时候需要进行三次握手&#xff0c;断开通讯的时候需要进行四次挥手。 1.seq(sequence number)&#xff0c;序列号&#xff0c;随机生成的 2.ack(acknowledgement number)&#xff0c;确认号&#xff0c;ackseq1 3.ACK(ackno…

Linux安装JenkinsCLI

项目简介安装目录 mkdir -p /opt/jenkinscli && cd /opt/jenkinscli JenkinsCLI下载 wget http://<your-jenkins-server>/jnlpJars/jenkins-cli.jar # <your-jenkins-server> 替换为你的 Jenkins 服务器地址 JenkinsCLI授权 Dashboard-->Configure Glob…

希尔贝壳入选“北京市人工智能大模型高质量数据集发布(第二批)”合作企业

8月28日&#xff0c;2023中国国际服务贸易交易会通用人工智能算力论坛在石景山区举办。论坛上&#xff0c;北京市人工智能大模型高质量数据集&#xff08;第二批&#xff09;发布&#xff0c;其中包含北京希尔贝壳科技有限公司的“大模型方言口语语音数据集”和“智能会议场景高…

评估安全 Wi-Fi 接入:Cisco ISE、Aruba、Portnox 和 Foxpass

在当今不断变化的数字环境中&#xff0c;对 Wi-Fi 网络进行强大访问控制的需求从未像现在这样重要。各组织一直在寻找能够为其用户提供无缝而安全的体验的解决方案。 在本博客中&#xff0c;我们将深入探讨保护 Wi-Fi&#xff08;和有线&#xff09;网络的四种领先解决方案——…

Show that f(z)=1/z is analytic or not

See https://brainly.in/question/21838444

HVV行动之态势感知平台(一)

知攻善防&#xff0c;遇强则强&#xff01; 先介绍一下什么是HVV行动&#xff1a; 它是由公安部牵头的&#xff0c;通过组织红队和蓝队进行为期两周到三周的攻防对抗演习&#xff0c;来检测一些企业单位可能存在的网络漏洞和威胁&#xff0c;进而进行修复和加固&#xff0c;提…

0829|C++day7 auto、lambda、C++数据类型转换、C++标准模板库(STL)、list、文件操作

一、思维导图 二、【试编程】将实例化类对象写入容器后&#xff0c;写入.txt文本中&#xff0c;再重新定义一个类容器&#xff0c;将.txt中的内容读取出来&#xff0c;输出到终端 封装一个学生的类&#xff0c;定义一个学生这样类的vector容器, 里面存放学生对象&#xff08;至…

什么是RESTful API,Spring MVC如何支持RESTful架构

文章目录 &#x1f388;个人主页&#xff1a;程序员 小侯 &#x1f390;CSDN新晋作者 &#x1f389;欢迎 &#x1f44d;点赞✍评论⭐收藏 ✨收录专栏&#xff1a;Java框架 ✨文章内容&#xff1a;Spring MVC支持RESTful架构 &#x1f91d;希望作者的文章能对你有所帮助&#xf…

ubuntu18.04.6的安装教程

目录 一、下载并安装virtualbox virtualbox7.0.8版本的安装 二、Ubuntu的下载与安装 ubuntu18.04.6操作系统 下载 安装 一、下载并安装virtualbox VirtualBox是功能强大的x86和AMD64/Intel64虚拟化企业和家庭使用的产品。VirtualBox不仅是面向企业客户的功能极其丰富的高…

ShardingSphere——弹性伸缩原理

摘要 支持自定义分片算法&#xff0c;减少数据伸缩及迁移时的业务影响&#xff0c;提供一站式的通用弹性伸缩解决方案&#xff0c;是 Apache ShardingSphere 弹性伸缩的主要设计目标。对于使用单数据库运行的系统来说&#xff0c;如何安全简单地将数据迁移至水平分片的数据库上…

线上批量查询物流导出到表格的操作指南

现在的生活中&#xff0c;我们经常需要查询包裹物流信息。如果一次性需要查询多个快递单号的物流信息&#xff0c;手动一个一个查询会非常麻烦。今天&#xff0c;我将向大家分享一个简单实用的方法&#xff0c;可以批量查询物流并导出到表格&#xff0c;方便随时查看。 首先&am…

【100天精通python】Day50:python web编程_Django框架使用

目录 1 安装Django Web框架 2 创建一个Django 项目 3 数据模型 3.1 在应用程序的 models.py 文件中定义数据模 3.2 创建模型的迁移文件并应用 3.2.1 查询模型对象&#xff1a; 3.2.2 创建新模型对象&#xff1a; 3.2.3 更新模型对象&#xff1a; 3.2.4 删除模型对象&a…

盘点狼人杀中的强神与弱神 并评价操作体验

最初 强神是大家对猎人的称呼&#xff0c;但随着板子的增加 强神渐渐变成了强神神牌的统称。 狼人杀发展至今板子已经非常多了&#xff0c;而每个板子都会有不同的角色。 相同的是 大部分都会希望拿到一张强力神牌&#xff0c;这样能大大提高我们玩家的游戏体验&#xff0c;但其…

【WebSocketIndexedDB】node+WebSocketIndexedDB开发简易聊天室

序幕介绍&#xff1a; WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 讲人话就是说&#xff1a;WebSocket 使得客户端和服务器之间的数据交换变得更加简单&#xff0c;在 WebSocket API 中&#xff0c;浏览器和服务器只需要完成一次握手&#x…

正中优配:沪指震荡涨0.23%,保险、酿酒等板块走强,半导体板块下挫

1日早盘&#xff0c;沪指、深成指盘中强势震动上扬&#xff0c;创业板指回落翻绿&#xff0c;科创50指数跌超1%&#xff1b;两市半日成交缺乏5000亿元。 到午间收盘&#xff0c;沪指涨0.23%报3127.19点&#xff0c;深成指涨0.25%&#xff0c;创业板指跌0.23%&#xff0c;科创5…

数字人创作+SadTalker+GTX1080

https://github.com/OpenTalker/SadTalker 开源项目 SadTalker模型是一个使用图片与音频文件自动合成人物说话动画的开源模型&#xff0c;我们自己给模型一张图片以及一段音频文件&#xff0c;模型会根据音频文件把传递的图片进行人脸的相应动作&#xff0c;比如张嘴&#xf…

Java面试之用两个栈实现队列

文章目录 题目一、什么是队列和栈&#xff1f;1.1队列1.2栈 二、具体实现2.1 思路分析2.2代码实现 题目 用两个栈实现一个队列&#xff0c;实现在队列尾部插入节点和在队列头部删除节点的功能。 一、什么是队列和栈&#xff1f; 1.1队列 队列是一种特殊的线性表&#xff0c;…

Linux 命令大全(下)

Linux 命令大全&#xff08;上&#xff09; 本文目录 6. 网络通讯 常用命令6.1 ssh 命令 – 安全的远程连接服务器6.1.1 含义6.1.2 语法格式6.1.3 常用参数6.1.4 参考示例 6.2 netstat 命令 – 显示网络状态6.2.1 含义6.2.2 语法格式6.2.3 常用参数6.2.4 参考示例 6.3 dhclient…