当前位置: 首页 > 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网络实现全自动无人值 守批量安装。 需求描述 > 服…...

病理HE学习贴(自备)

目录 正常结构 癌症HE 在线学习 以胃癌的学习为例 正常结构 1&#xff1a;胃粘膜正常结构和细胞分化 ●表面覆盖小凹上皮细胞(主要标志物&#xff1a;MUC5AC)以保护黏膜。 ●胃底腺固有腺体由黏液颈细胞(MUC6)、主细胞(Pepsinogen l)和壁细胞(Proton pump α-subunit)组…...

关于协同过滤算法在物联网的应用-基于用户行为数据和物联网设备数据,以此提供个性化的智能家居控制推荐服务

关于协同过滤算法在物联网领域的应用的一个案例是基于用户行为数据和物联网设备数据&#xff0c;为用户提供个性化的智能家居控制推荐服务。 具体实现如下&#xff1a; 数据收集&#xff1a;收集用户对智能家居设备的使用行为数据&#xff0c;包括设备的打开、关闭、调节等操…...

计算机网络(6):应用层

每个应用层协议都是为了解决某一类应用问题&#xff0c;而问题的解决又往往是通过位于不同主机中的多个应用进程之间的通信和协同工作来完成的。 应用层的具体内容就是规定应用进程在通信时所遵循的协议。 应用层的许多协议都是基于客户服务器方式。即使是对等通信方式&#x…...

ESP32:整合存储配网信息和MQTT笔记

文章目录 1.给LED和KEY的所用IO增加配置项1.1 增加配置文件1.2 修改相应的c源码 2. 把mqtt\tcp的工程整合到一起2.1 在何处调用 mqtt_app_start() 3. 测试MQTT4. 完整的工程源码 有一段时间没有玩ESP32&#xff0c;很多知识点都忘记了。今天测试一下MQTT&#xff0c;做个笔记。…...

nginx源码分析-4

这一章内容讲述nginx的模块化。 ngx_module_t&#xff1a;一个结构体&#xff0c;用于描述nginx中的各个模块&#xff0c;其中包括核心模块、HTTP模块、事件模块等。这个结构体包含了一些模块的关键信息和回调函数&#xff0c;以便nginx在运行时能够正确地加载和管理这些模块。…...

【Unity美术】Unity工程师对3D模型需要达到的了解【二】

&#x1f468;‍&#x1f4bb;个人主页&#xff1a;元宇宙-秩沅 &#x1f468;‍&#x1f4bb; hallo 欢迎 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍&#x1f4bb; 本文由 秩沅 原创 &#x1f468;‍&#x1f4bb; 收录于专栏&#xff1a;Uni…...

《微信小程序开发从入门到实战》学习六十九

6.6 网络API 6.6.3 下载文件API 使用 wx.downloadFile 接口是可使小程序发起HTTPS GET请求&#xff0c;下载文件到手机端。 单次下载的最大文件为50MB。接受Obkect参&#xff0c;参支持属性如下&#xff1a; url&#xff08;必填&#xff09;&#xff1a;下载文件的URL he…...

2022年全球软件质量效能大会(QECon北京站2022)-核心PPT资料下载

一、峰会简介 当前&#xff0c;新一轮科技革命和产业变革正在重塑全球经济格局&#xff0c;以云计算为代表的新一代信息技术创新活跃&#xff0c;与实体经济深度融合&#xff0c;推动泛在连接、数据驱动、智能引领的数字经济新形式孕育而生。 新兴技术的出现给测试乃至整个软…...

ILI9481 TFT3.5寸屏STM32F446ZEXX FMC驱动方式详解

图片来源于网络&#xff0c;如若侵权请联系博主删除 文章目录 1. 背景2. 基础知识2.1 TFT-LCD2.2 硬件接线2.3 FMC2.4 ILI9481 3. 软件抽象 1. 背景 最近做项目需要&#xff0c;博主在某宝上买了一块3.5寸的TFT屏&#xff0c;店家虽然发了资料&#xff0c;但是往产品上移植驱动…...

010、切片

除了引用&#xff0c;Rust还有另外一种不持有所有权的数据类型&#xff1a;切片&#xff08;slice&#xff09;。切片允许我们引用集合中某一段连续的元素序列&#xff0c;而不是整个集合。 考虑这样一个小问题&#xff1a;编写一个搜索函数&#xff0c;它接收字符串作为参数&a…...