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

ThreadLocal共享变量

一、ThreadLocal

我们知道多线程访问同一个共享变量时,会出现线程安全问题,为了保证线程安全开发者需要对共享变量的访问操作进行适当的同步操作,如加锁等同步操作。

除此之外,Java提供了ThreadLocal类,当一个共享变量使用ThreadLocal声明时,它表明,当每个线程访问共享变量时,会把共享变量复制一份到线程的工作内存,之后线程对此共享变量进行操作时操作的都是线程工作内存的变量而不是主内存中的共享变量,从而不需要加锁的同步操作实现避免出现线程安全问题。

二、Thread使用代码示例
public class ThreadLocalTest {private static ThreadLocal<String> variable = new ThreadLocal<>(); // (1) public static void main(String[] args) throws InterruptedException {variable.set(Thread.currentThread().getName());  // (2)// 创建线程一var thread1 = new Thread(() -> {System.err.println("Thread Name before set: " + Thread.currentThread().getName() + " " + variable.get());  // (3)variable.set(Thread.currentThread().getName()); // (4)System.err.println("Thread Name after set: " + Thread.currentThread().getName() + " " + variable.get()); // (5)});var thread2 = new Thread(() -> {System.err.println("Thread Name before set: " + Thread.currentThread().getName() + " " + variable.get()); // (6)variable.set(Thread.currentThread().getName()); // (7)System.err.println("Thread Name after set: " + Thread.currentThread().getName() + " " + variable.get()); // (8)});thread1.start();  // (9)thread2.start(); // (10)Thread.sleep(2000); // (11)System.err.println("main thread: " + variable.get()); // (12)}
}

输出:

Thread2 before set: Thread-1 null
Thread2 after set: Thread-1 Thread-1
Thread1 before set: Thread-0 null
Thread1 after set: Thread-0 Thread-0
main thread: main

示例中我们创建了两个线程,每个线程里都读取和设置全局的ThreadLcoal变量:

代码(1)创建了一个ThreadLocal共享变量variable,这里其实设置的是主线程工作内存里的共享变量副本

代码(2)主线程设置ThreadLocal变量variable

代码(3)线程一读取共享变量variable的值

代码(4)线程一设置共享变了variable的值,这里其实设置的是线程一工作内存里的共享变量副本

代码(5)线程一再次读取共享变量variable的值

代码(6)线程二读取共享变量variable的值

代码(7)线程二设置共享变了variable的值,这里其实设置的是线程二工作内存里的共享变量副本

代码(8)线程二再次读取共享变量variable的值

代码(9)启动线程一

代码(10)启动线程二

代码(11)主线程休眠2秒

代码(12)主线程读取共享变量variable的值

从输出我们可以看到,每个两个线程所操作的ThreadLocal变量互不影响,其实每个线程在设置和读取共享变量variable时操作的都是共享变量在线程自己工作内存里的副本,并不会影响到其他线程的值。

三、ThreadLocal原理

我们说线程操作ThreadLocal类型的变量时,会复制一个变量副本到线程工作空间,然后所有操作都是对副本变量进行的。那线程是怎么复制ThreadLocal变量到线程工作空间的,线程和ThreadLocal之前是怎么关联的。首先我们来看一看Thread的结构

Thread

可以看到Thread类有很多属性,我们现在只关心threadLocalsinheritableThreadLocals,这两个变量都是ThreadLocalMap类型的实例。TThreadLocalMap是一个ThreadLocal.ThreadLocalMap类型,这是一个特殊的Map。

首先看一下在前面的例子中我们是怎么在线程中使用ThreadLocal变量的,

variable.set(Thread.currentThread().getName()); // 设置ThreadLocal变量
variable.get();    // 读取ThreadLocal变量

接下来我们看看ThreadLocal变量的set和get方法。

ThreadLocal.get()相关源码如下:

public T get() {return get(Thread.currentThread()); // (1)
}private T get(Thread t) {ThreadLocalMap map = getMap(t);  // (2)if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this); // (3)if (e != null) {@SuppressWarnings("unchecked")T result = (T) e.value;return result;}}return setInitialValue(t);   // (4)
}ThreadLocalMap getMap(Thread t) {return t.threadLocals;    // (5)
}

从代码(1)可以看到,调用ThreadLocal的get()方法时,会将当前线程作为参数传递。代码(2)调用getMap方法获取ThreadLocalMap类型变量,如果map不为空则把ThreadLocal实例作为key获取值,这个值就是ThreadLocal变量的值(5)可以看到getMap方法返回的就是Thread类型的threadLocals变量。根据上述分析我们可以知道:

线程在读取ThreadLocal变量时,实际是获取当前线程的threadLocals变量,然后把ThreadLocal实例当做key从threadLocals查询对应的值。也就是说线程读取的ThreadLocal的实际值并不是存在ThreadLocal实例里的,而是存在线程的threadLocals里面,threadLocals是一个ThreadLocal.ThreadLocalMap,这是一个特殊的Map,key为ThreadLocal实例,值为ThreadLocal变量的实际值。ThreadLoca相当于一个转接口,连接Thread和ThreadLocal。

代码(4)可以看到如果当前线程的threadLocals变量为null,会调用ThreadLocal的setInitialValue方法初始化当前线程的threadLocals实例。

private T setInitialValue(Thread t) {T value = initialValue();ThreadLocalMap map = getMap(t);if (map != null) {map.set(this, value);} else {createMap(t, value);}if (this instanceof TerminatingThreadLocal<?> ttl) {TerminatingThreadLocal.register(ttl);}if (TRACE_VTHREAD_LOCALS) {dumpStackIfVirtualThread();}return value;
}void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue); // (1)
}

setInitialValue方法会创建参数传递线程的threadLocals值,并且设置一个初始化值。从代码(1)可以看到threadLocals的key为ThreadLocal实例。

下面再看看ThreadLocal的set方法:

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

从代码(1)可以看到,调用ThreadLocald的set方法会向当前线程的threadLocals变量里设置传递的值value,key为ThreadLocal实例的引用,和get方法一样,如果当前线程的threadLocals变量为null,则会创建一个ThreadLocalMap变量并把value设置为初始值。

总结:在每个线程内部都有一个threadLocals变量,该变量类型为ThreadLocal.ThreadLocalMap,其中key为我们定义的ThreadLocal变量的this引用,value则为我们使用set方法设置的值。每个线程的本地变量存放在线程自己的内存变量threadLocals中。

如果线程不销毁,那么对应的本地变量就会一直存在,所以可能存在内存溢出,因此使用完毕之后要记得调用ThreadLocal的remove方法删除对应线程的threadLocals变量里的值。

注意:ThreadLocal不具备继承性,也就是说子线程并不能访问父线程的ThreadLocal变量。

相关文章:

ThreadLocal共享变量

一、ThreadLocal 我们知道多线程访问同一个共享变量时&#xff0c;会出现线程安全问题&#xff0c;为了保证线程安全开发者需要对共享变量的访问操作进行适当的同步操作&#xff0c;如加锁等同步操作。 除此之外&#xff0c;Java提供了ThreadLocal类&#xff0c;当一个共享变…...

前端crypto-js 库: MD5

文章目录 什么是crypto-js安装依赖MD5 什么是crypto-js github地址: https://github.com/brix/crypto-js cryptojs文档: https://cryptojs.gitbook.io/docs/#encoders CryptoJS (crypto.js) 为 JavaScript 提供了各种各样的加密算法。 CryptoJS是一个JavaScript加密算法库&a…...

2024新年快乐

2024-1-1 祝福大家和自己健康喜乐&#xff0c;升职加薪&#xff0c;新年快乐 页面加载事件load 我们页面加载事件的触发是等所有的资源加载完毕时触发该事件。和click一样是事件&#xff0c;但是触发时机是等资源加载&#xff08;浏览器&#xff09;完毕。这个事件我们可以将…...

OpenCV-Python(21):轮廓特征及周长、面积凸包检测和形状近似

2. 轮廓特征 轮廓特征是指由轮廓形状和结构衍生出来的一些特征参数。这些特征参数可以用于图像识别、目标检测和形状分析等应用中。常见的轮廓特征包括&#xff1a; 面积&#xff1a;轮廓所包围的区域的面积。周长&#xff1a;轮廓的周长&#xff0c;即轮廓线的长度。弧长&…...

连接progressql报错Cannot load JDBC driver class ‘org.postgresql.Driver‘,亲测有效!!!

Jmeter连接progressql报错Cannot load JDBC driver class ‘org.postgresql.Driver’ 1.到官方下载驱动注意&#xff1a;根据项目的JDK版本来下载对应的驱动Download | pgJDBC 2.将postgresql-42.2.27.jar复制到lib目录下面&#xff0c; 然后重新启动 连接driver信息如下&#…...

SQLAlchemy快速入门

安装依赖 pip install sqlalchemy pip install pymysql创建数据库和表 # 创建数据库 drop database if exists sqlalchemy_demo; create database sqlalchemy_demo character set utf8mb4; use sqlalchemy_demo;# 创建表 drop table if exists user; create table user (id …...

java 纯代码导出pdf合并单元格

java 纯代码导出pdf合并单元格 接上篇博客 java导出pdf&#xff08;纯代码实现&#xff09; 后有一部分猿友叫我提供一下源码&#xff0c;实际上我的源码已经贴在帖子上了&#xff0c;都是同样的步骤&#xff0c;只是加多一点设置就可以了。今天我再次上传一下相对情况比较完整…...

Linux自己的应用商店yum

&#x1f4ab;Linux系统如何安装软件 在Linux系统中我们可以通过多种方式安装软件&#xff0c;常见方式有以下三种&#xff1a;   1.源代码安装   2.rpm包安装   3.使用yum软件包管理器安装   早期人们通过下载软件源代码&#xff0c;然后再经过交叉编译等一系列工作下…...

集成电路模拟设计——【基于Serdes 应用的 串化/解串器 时钟与数据恢复电路CDR】

串化/解串器 & 时钟与数据恢复电路CDR&#xff08;可提供实现过程、仿真波形与具体参数细节 本文内容摘要背景串化/解串器全速树形串化器半速树形串化器全速移位寄存器串化器多级树形解串器 PLL型CDR整体架构实现结果 Bang-Bang型CDR整体架构 PS/PI型CDR电路PS电路设计PI电…...

OpenWrt 编译入门(小白版)

编译环境 示例编译所用系统为 Ubuntu 22.04&#xff0c;信息如下 编译时由于网络问题&#xff0c;部分软件包可能出现下载问题&#xff0c;还请自备网络工具或尝试重新运行命令 编译步骤 下图为官网指示 编译环境设置&#xff08;Build system setup&#xff09; 这里根据我…...

嵌入式视频播放器(mplayer)

1.文件准备&#xff1a; MPlayer-1.0rc2.tar.bz2 libmad-0.15.1b.tar.gz 直接Git到本地 git clone https://gitee.com/zxz_FINE/mplayer_tarball.git 2.文件夹准备&#xff1a; src存放解压后的源码文件&#xff0c;target_Mplayer存放编译安装的目标文件 mkdir src targe…...

对房价数据集进行处理和数据分析

大家好&#xff0c;我是带我去滑雪&#xff0c;每天教你一个小技巧&#xff01; 房价数据集通常包含各种各样的特征&#xff0c;如房屋面积、地理位置、建造年份等。通过对数据进行处理和分析&#xff0c;可以更好地理解这些特征之间的关系&#xff0c;以及它们对房价的影响程度…...

BERT的学习

BERT 1.前言 self-supervised learning是一种无监督学习的特殊形式&#xff0c;算法从数据本身生成标签或者目标&#xff0c;然后利用这些生成的目标来进行学习。&#xff08;也就是说数据集的标签是模型自动生成的&#xff0c;不是由人为提供的。&#xff09;例如&#xff0…...

数据结构OJ实验9-图存储结构和遍历

A. 图综合练习--构建邻接表 题目描述 已知一有向图&#xff0c;构建该图对应的邻接表。 邻接表包含数组和单链表两种数据结构&#xff0c;其中每个数组元素也是单链表的头结点&#xff0c;数组元素包含两个属性&#xff0c;属性一是顶点编号info&#xff0c;属性二是指针域n…...

20231226在Firefly的AIO-3399J开发板上在Android11下调通后摄像头ov13850

20231226在Firefly的AIO-3399J开发板上在Android11下调通后摄像头ov13850 2023/12/26 8:22 开发板&#xff1a;Firefly的AIO-3399J【RK3399】 SDK&#xff1a;rk3399-android-11-r20211216.tar.xz【Android11】 Android11.0.tar.bz2.aa【ToyBrick】 Android11.0.tar.bz2.ab And…...

0101包冲突导致安装docker失败-docker-云原生

文章目录 1 前言2 报错3 解决结语 1 前言 最近在学习k8s&#xff0c;前置条件就是要安装指定版本的docker&#xff0c;命令如下 yum install -y docker-ce-20.10.7 docker-ce-cli-20.10.7 containerd.io-1.4.62 报错 file /usr/libexec/docker/cli-plugins/docker-buildx fr…...

【力扣100】17.电话号码的字母组合

添加链接描述 class Solution:def letterCombinations(self, digits: str) -> List[str]:# 思路是使用回溯算法if not digits:return []phone {2:[a,b,c],3:[d,e,f],4:[g,h,i],5:[j,k,l],6:[m,n,o],7:[p,q,r,s],8:[t,u,v],9:[w,x,y,z]}def backtrack(con,dig):# 收获if le…...

2023。

一月 从头开始 二月 准备复试&初试成绩 三月 最开心 过了两个生日&#xff08;这机率&#xff0c;幸运儿&#xff09; 考研也成功上岸&#xff01;nnuGISer! 四月 和室友去了趟武汉 五月 拍毕业照 六月 人生高光时刻 省创&#xff01;上台领奖&#xff01;考研…...

出现 Cause: java.sql.SQLException: Field ‘id‘ doesn‘t have a default value解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 在驱动Springboot项目的时候,出现如下问题: org.springframework.dao.DataIntegrityViolationException: ### Error updating database. Cause: java.sql...

Linux--批量自动装机

实验环境 随着某公司业务不断发展&#xff0c;服务器主机的数量也迅速增长&#xff0c;对于功能变更或新采购的服务器&#xff0c; 需要重新安装CentOS7操作系统&#xff0c;为了提高服务器装机效率&#xff0c;要求基于PXE网络实现全自动无人值 守批量安装。 需求描述 > 服…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中&#xff0c;时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志&#xff0c;到供应链系统的物流节点时间戳&#xff0c;时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库&#xff0c;其日期时间类型的…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

ssc377d修改flash分区大小

1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

STM32标准库-DMA直接存储器存取

文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA&#xff08;Direct Memory Access&#xff09;直接存储器存取 DMA可以提供外设…...

WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)

一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解&#xff0c;适合用作学习或写简历项目背景说明。 &#x1f9e0; 一、概念简介&#xff1a;Solidity 合约开发 Solidity 是一种专门为 以太坊&#xff08;Ethereum&#xff09;平台编写智能合约的高级编…...

中医有效性探讨

文章目录 西医是如何发展到以生物化学为药理基础的现代医学&#xff1f;传统医学奠基期&#xff08;远古 - 17 世纪&#xff09;近代医学转型期&#xff08;17 世纪 - 19 世纪末&#xff09;​现代医学成熟期&#xff08;20世纪至今&#xff09; 中医的源远流长和一脉相承远古至…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...