当前位置: 首页 > news >正文

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方法源码如下 : (PS : 注意此处是泛型在方法上的应用,而不是自定义泛型方法)

    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的一些浅显解读。感谢阅读!

相关文章:

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&#xff0c;本…...

前端实习day37~day38

昨晚太累了&#xff0c;就没有写博客&#xff0c;今天一起写好了&#xff0c;在昨天和今天的努力下&#xff0c;终于把业务模型的基本版本跑通了&#xff0c;明天再补充一下小接口&#xff0c;然后再把一些异常情况判断一下&#xff0c;争取明天弄完&#xff0c;然后早点下班&a…...

题目:2635.转换数组中的每个元素

​题目来源&#xff1a; leetcode题目&#xff0c;网址&#xff1a;2635. 转换数组中的每个元素 - 力扣&#xff08;LeetCode&#xff09; 解题思路&#xff1a; 按要求模拟即可。 解题代码&#xff1a; /*** param {number[]} arr* param {Function} fn* return {number[]}…...

Docker Compose具体应用

文章目录 介绍安装和配置编写docker-compose.yml文件docker-compose执行时注意事项常用命令和操作高级特性和扩展总结 介绍 Docker Compose的概述 Docker Compose是一个用于定义和运行多容器Docker应用程序的工具。它使用YAML文件来配置应用程序需要的服务、网络和卷等资源。 …...

FastAPI 参数的作用

FastAPI是一个现代化的Python web框架&#xff0c;其参数具有重要的作用。在FastAPI中&#xff0c;参数被用于接收HTTP请求中的数据及其它相关信息。 FastAPI支持的参数类型包括&#xff1a; 查询参数&#xff08;query parameters&#xff09; 查询参数是指将参数附加到URL末…...

国内免费无限制的chatgpt导航和ai画画

非常实用的AI网址导航&#xff0c;其实际使用体验非常便捷。该导航系统不仅提供了全面的网站分类和搜索功能&#xff0c;还对每个网站进行了精准的评估和排序。推荐高质量的网站资源&#xff0c;并实时检测网站的安全性&#xff0c;保障用户的上网安全。 总的来说&#xff1a…...

【USRP】集成化仪器系列2 :示波器,基于labview实现

USRP 示波器 1、设备IP地址&#xff1a;默认为192.168.10.2&#xff0c;请勿 修改&#xff0c;运行阶段无法修改。 2、中心频率&#xff1a;当需要生成不同频率单载波的 时候请直接修改中心频率&#xff0c;在运行的时候您 也可以直接修改中心频率。 3、接收增益&#xff1a;…...

Linux map type uncache 和 write combine区别

文章目录 前言一、定义二、隐含区别总结 前言 这段时间被Map Cache Type坑了一次。 GPU的PCI bar地址map成uncache 的还是 write combine&#xff1f; 一、定义 uncache(uc) &#xff1a; map后&#xff0c;CPU读写不经过Cache write combine(wb): map后&#xff0c;CPU读写同…...

【业务功能篇93】微服务-springcloud-多线程-异步处理-异步编排-CompletableFutrue-实战运用

异步处理编排 我们可以在商品详细信息查询的位置实现CompletableFuture的异步编排处理。 根据业务分析&#xff1a;3.4.5数据接口的入参信息需要来源于1数据接口的返回信息&#xff0c;也就是skuid 所以可以设计 1 3 4 5 串行线程 &#xff0c;而 3 4 5依赖1 &#xff0c;需要等…...

哈希的应用——位图

文章目录 前言1. 面试题思考2. 位图2.1 位图的概念2.2 思路讲解及代码实现结构定义构造函数set和reset接口实现set和reset测试观察test接口实现test接口测试思考 3. 位图的应用习题1习题2习题3 4. 总结5. 源码5.1 bitset.h5.2 Test.c 前言 前面的文章里我们学习了哈希表&#x…...

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书对外经济贸易大学图书馆

2023开学礼《乡村振兴战略下传统村落文化旅游设计》许少辉八一新书对外经济贸易大学图书馆...

合并两个有序链表(每日一题)

“路虽远&#xff0c;行则将至” ❤️主页&#xff1a;小赛毛 ☕今日份刷题&#xff1a;合并两个有序链表 题目描述&#xff1a; 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例1&#xff1a; 输入&#xff1a;l1 …...

React Hooks总览

总览 hooks 功能分类具体 hooks具体功能React v18新特性跨端支持数据更新驱动useState定义要在页面中渲染的数据❌✔useReducer定义要在页面中渲染的数据&#xff0c;且这个数据有多种处理逻辑❌✔useSyncExternalStoreconcurrent 模式下&#xff0c;订阅外部 store 的行为&am…...

风向变了!智能汽车何以「降本」

随着软件定义汽车的概念逐步落地&#xff0c;以及底盘、动力、座舱、智驾、车身等不同域&#xff08;分布式或者混合式&#xff09;的功能更新迭代和融合&#xff0c;汽车行业正在意识到&#xff1a;底层硬件架构重构的迫切性。 事实上&#xff0c;早在2016年&#xff0c;作为传…...

后端面试话术集锦第 十五 篇:java线程面试话术

这是后端面试集锦第十五篇博文——java线程面试话术❗❗❗ 1. 创建线程的方式 首先呢,Thread类本质上是实现了Runnable接口,代表一个线程的实例。 所以,我们可以编写一个类,继承Thread类,或者直接实现Runnable接口。然后,再重写下~run方法就行了。启动线程的方式就是调…...

cocos creator配置终端调试

在launch.json里添加"preLaunchTask":“CocosCreator compile” 在cocos creator里选择开发者&#xff0c;visual studio code工作流&#xff0c;选择添加编译任务。 添加 settings.json {"files.exclude":{"**/.git": true,"**/.DS_Sto…...

达梦类型转换问题-float转换为varchar

表结构 CREATE TABLE "SYSDBA"."TABLE_2" ( "COLUMN_1" FLOAT, "COLUMN_2" NUMERIC(22,6)) STORAGE(ON "MAIN", CLUSTERBTR) ; 表数据&#xff1a; 查询&#xff0c;将numeric转换为float&#xff0c;再转换为varchar&…...

怎么用postman连接websocket

点击右侧栏的Collections&#xff0c;然后点击旁边的New&#xff0c;然后点击其中的WebSocket Request,然后输入Url&#xff0c;点击Connection&#xff0c;这里需要注意的是Url不能加上http://&#xff0c;因为这个不是http协议。...

需求分析入门

认识管理软件 什么是管理软件 管理软件就是用来辅助企业进行管理的软件&#xff0c;既包括对企业“人、财、物”相关的资产信息的管理&#xff0c;也包括对企业“供、产、销”相关的业务活动信息的管理。管理软件的重点在于管理信息的收集、流转&#xff0c;资源的共享、集成…...

攻防世界-php_rce

原题 解题思路 thinkPHP.0有漏洞&#xff0c;ThinkPHP5.x rec 漏洞分析与复现。本题就是利用漏洞查找。格式是&#xff1a; ?sindex/\think\app/invokefunction&functioncall_user_func_array&vars[0]system&vars[1][]命令。 ls查看文件没什么东西&#xff0c;r…...

最小生成树Kruskal、Prim算法C++

什么是最小生成树 连通图&#xff1a; 在无向图中&#xff0c;若从顶点v1到顶点v2有路径&#xff0c;则称顶点v1和顶点v2是连通的。如果图中任意一对顶点都是连通的&#xff0c;则称此图为连通图。 生成树&#xff1a; 一个连通图的最小连通子图称作为图的生成树。有n个顶点的…...

系统架构设计师-计算机系统基础知识(2)

目录 一、存储管理 1、页式存储 2、段式存储 3、段页式存储 二、磁盘管理 1、先来先服务FCFS 2、最短寻道时间优先SSTF 三、文件系统 1、文件基本概念 2、文件的类型&#xff1a; 3、索引文件结构 4、位示图 四、性能指标 五、性能设计 1、阿姆达尔定律 六、性能评估 1、…...

二叉树的介绍

写在前面&#xff1a; 二叉树是数据结构课程中非常重要的内容&#xff0c;我们针对二叉树的概念、性质以及类型展开详细介绍。 一、概念 二叉树&#xff08;Binary Tree&#xff09;是n&#xff08;n>0&#xff09;个结点的有限集合&#xff0c;该集合或者空集&#xff0…...

数据结构与算法复杂度介绍

目录 一、基本概念 二、时间复杂度 【2.1】时间复杂度概念 【2.2】大O的渐进表示法 【2.3】举例时间复杂度计算 三、空间复杂度 一、基本概念 数据结构&#xff1a;相互之间存在一种或者多种特定关系的数据元素的集合。在逻辑上可以分为线性结构&#xff0c;散列结构、树…...

CentOS 安装蒲公英

官方教程链接&#xff1a; https://service.oray.com/question/5063.html 教程使用的是2.3版本&#xff0c;官网下载的最新版是2.4&#xff0c;所以命令会有所不同 安装成功后&#xff0c; 任意路径下执行pgyvisitor&#xff0c;调出交互界面pgyvisitor login&#xff0c;登录…...

英语语法基础--思维导图

思维导图通常用于可视化和整理信息&#xff0c;而英文语法非常广泛且复杂&#xff0c;无法在一个简单的思维导图中完整表示。然而&#xff0c;我可以提供一个简化版本的英文语法思维导图&#xff0c;列出一些主要的语法概念和部分示例。 请注意&#xff0c;这只是一个基本的概…...

Android泛型详解

参考文献&#xff1a;https://pingfangx.github.io/java-tutorials/java/generics/types.html 1&#xff0c;什么是泛型&#xff1f; Java泛型(generics)是JDK5中引入的一个新特性&#xff0c;泛型提供了 编译时类型安全检测机制&#xff0c; 该机制允许程序员在编译时检测到…...

C++信息学奥赛1178:成绩排序

#include<bits/stdc.h> using namespace std; int main(){int n;cin>>n; // 输入整数 n&#xff0c;表示数组的大小int arr[n]; // 创建大小为 n 的整型数组 arrstring brr[n]; // 创建大小为 n 的字符串数组 brrfor(int i0;i<n;i) cin>>brr[i]>>ar…...

【计算机视觉 | 目标检测】目标检测常用数据集及其介绍(七)

文章目录 一、Cops-Ref二、FAT (Falling Things)三、GEN1 Detection (Prophesee GEN1 Automotive Detection Dataset)四、RIT-18五、AGAR (Annotated Germs for Automated Recognition)六、EuroCity Persons七、Freiburg Groceries八、Lytro Illum九、PFN-PIC (PFN Picking Ins…...

100天精通Golang(基础入门篇)——第20天:Golang 接口 深度解析☞从基础到高级

&#x1f337;&#x1f341; 博主猫头虎&#x1f405;&#x1f43e; 带您进入 Golang 语言的新世界✨✨&#x1f341; &#x1f984; 博客首页——&#x1f405;&#x1f43e;猫头虎的博客&#x1f390; &#x1f433; 《面试题大全专栏》 &#x1f995; 文章图文并茂&#x1f…...