用ThreadLocal解决线程隔离问题

存在的以下代码所示的线程隔离问题:

package study.ThreadLocal解决线程隔离问题;/*线程隔离 - 在多线程并发场景下,每个线程的变量都应该是相互独立的线程A:设置(变量1) 获取(变量1)线程B:设置(变量2) 获取(变量2)*/public class 存在的线程隔离问题 {private String content;private String getContent() {return content;}private void setContent(String content) {this.content = content;}public static void main(String[] args) {存在的线程隔离问题 demo = new 存在的线程隔离问题();// 多个线程同时访问和修改同一个 存在的线程隔离问题 类对象 demo 的属性 content,这会导致线程之间的竞争问题。// 由于 content 变量是共享的,并且没有任何同步控制,所以多个线程可能会相互覆盖彼此的数据,从而导致不可预测的结果。for (int i = 0; i < 10; i++) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {/*每个线程:存一个变量,过一会再取出这个变量*/demo.setContent(Thread.currentThread().getName() + "的数据");System.out.println("---------------------------");System.out.println(Thread.currentThread().getName() + "------>" + demo.getContent());}});thread.setName("线程" + i); // 给每个线程设置线程名thread.start();}}
}

结果:
在这里插入图片描述

使用ThreadLocal解决上面线程隔离问题。

package study.ThreadLocal解决线程隔离问题;/*线程隔离 - 在多线程并发场景下,每个线程的变量都应该是相互独立的线程A:设置(变量1) 获取(变量1)线程B:设置(变量2) 获取(变量2)ThreadLocal:1. set() : 将变量绑定到当前线程中2. get() : 获取当前线程绑定的变量*/public class 存在的线程隔离问题 {ThreadLocal<String> tl = new ThreadLocal<>();private String content;private String getContent() {return tl.get();}private void setContent(String content) {tl.set(content);
//        this.content = content;}public static void main(String[] args) {存在的线程隔离问题 demo = new 存在的线程隔离问题();// 多个线程同时访问和修改同一个 存在的线程隔离问题 类对象 demo 的属性 content,这会导致线程之间的竞争问题。// 由于 content 变量是共享的,并且没有任何同步控制,所以多个线程可能会相互覆盖彼此的数据,从而导致不可预测的结果。for (int i = 0; i < 10; i++) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {/*每个线程:存一个变量,过一会再取出这个变量*/demo.setContent(Thread.currentThread().getName() + "的数据");System.out.println("---------------------------");System.out.println(Thread.currentThread().getName() + "------>" + demo.getContent());}});thread.setName("线程" + i); // 给每个线程设置线程名thread.start();}}
}

结果:
在这里插入图片描述
如果使用synchronized也可以完成一样的效果,但是却牺牲了程序的并发性。

synchronized(存在的线程隔离问题.class) {demo.setContent(Thread.currentThread().getName() + "的数据");System.out.println("---------------------------");System.out.println(Thread.currentThread().getName() + "------>" + demo.getContent());
}

在这里插入图片描述


另一个需求场景:
事务的使用注意点:

  1. service层和dao层的连接对象保持一致
  2. 每个线程的connection对象必须前后一致,并且不同线程之间线程隔离,你处理你的,我处理我的,互不干扰。

常规的解决方案:

  1. 传参:将service层的connection对象直接传递到dao层
  2. 加锁

弊端

  1. 将 Connection 对象直接从 service 层传递到 DAO 层确实会提高耦合度,这是因为这种做法违反了分层架构的原则,使各层之间的依赖关系更加紧密。耦合度(Coupling)是指两个模块或类之间互相依赖的程度。高耦合意味着模块之间的依赖关系较强,任何一个模块的变化都会影响到其他模块。低耦合则意味着模块之间的依赖关系较弱,模块可以独立地进行修改和维护。
  2. 因为存在加锁的同步,使得在多线程环境下降低并发度从而降低程序性能。

使用ThreadLocal相比常规的解决方案的优势

  1. 传递数据:保存每个线程绑定的数据,在需要的地方可以直接获取,避免参数直接传递带来的代码耦合问题
  2. 线程隔离:各线程之间的数据相互隔离又兼具并发性,避免同步方式带来的性能损失

内部结构

在这里插入图片描述

  • 早期的ThreadLocal设计是ThreadLocal来维护ThreadLocalMap,每个Thread类线程对象作为Map的Key
  • 而JDK8的设计是让每一个Thread类线程对象来维护ThreadLocalMap,当前线程的每一个ThreadLocal作为Map的Key

好处:

  1. 实际开发中,ThreadLocal的数量往往少于线程Thread的数量,所以对于JDK8这种设计来说,每个Map存储的Entry的数量就会变少,这样就可以尽量避免哈希冲突的发生
  2. 当Thread销毁的时候,ThreadLocal也会随之销毁,从而能够及时回收内存

核心方法

在这里插入图片描述

set方法

  1. 首先获取当前线程,并根据当前线程获取它的Map
  2. 如果获取的Map不为空,则将此参数设置到Map中(当前ThreadLocal的引用作为Key)
  3. 如果Map为空,则给该线程创建Map,并设置初始值
/ *设置当前线程对应的ThreadLocal的值value是将要保存在当前线程对应的ThreadLocal的值
*/
public void set(T value) {// 获取当前线程对象Thread t = Thread.currentThread();// 获取当前线程对象中维护的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 判断map是否存在if (map != null) {// 存在则调用map.set设置此实体entry// 当前的ThreadLocal作为Key// 当前线程需要绑定的值作为Valuemap.set(this, value);} else {// 1) 当前线程Thread不存在ThreadLocalMap对象// 2) 则调用createMap进行ThreadLocalMap对象初始化// 3) 并将t(当前线程)和value(t对应的值)作为第一个entry存放进ThreadLocalMap中createMap(t, value);}}/* 获取当前线程Thread对应维护的ThreadLocalMapt就是当前线程返回对应维护的ThreadLocalMap
*/
ThreadLocalMap getMap(Thread t) {return t.threadLocals;}
// Thread.java源码中,关于threadLocals的描述如下
/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);}

get方法

  1. 首先获取当前线程,根据当前线程获取一个Map
  2. 如果获取的Map不为空,则在Map中以ThreadLocal的引用作为key来从Map中获取对应的Enrty e,否则转到4.
  3. 如果e不为null,则返回e.value,否则转到4.
  4. Map为空或者e为空,则通过initialValue函数获取初始值value(子类不重写的话,默认就是null了),然后用ThreadLocal的引用和初始值value作为新建Map的第一个key和value。
/*返回当前线程中保存的ThreadLocal的值如果当前线程没有此ThreadLocal变量,则它会通过initialValue方法进行初始化返回当前线程对应的ThreadLocal的值
*/
public T get() {// 获取当前线程对象Thread t = Thread.currentThread();// 获取此线程对象中维护的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 如果此map存在if (map != null) {// 因为map的Key是用ThreadLocal作为Key// 所以以当前的ThreadLocal为Key,调用getEntry获取对应的存储实体eThreadLocalMap.Entry e = map.getEntry(this);// 对e进行判空if (e != null) {@SuppressWarnings("unchecked")// 获取存储实体e对应的value值// 即为我们想要的当前线程对应ThreadLocal绑定的那个值T result = (T)e.value;return result;}}/*会进行如下初始化的两种情况第一种情况:map不存在,表示此线程没有维护的ThreadLocalMap对象第二种情况:map存在,但是没有与当前ThreadLocal关联的entry*/return setInitialValue();}/*初始化返回初始化后的值
*/private T setInitialValue() {// 调用initialValue获取初始化后的值// 此方法可以被子类重写,如果不重写默认返回nullT value = initialValue();// 获取当前线程对象Thread t = Thread.currentThread();// 获取当前线程对象中维护的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 判断map是否存在if (map != null) {map.set(this, value);} else {createMap(t, value);}if (this instanceof TerminatingThreadLocal) {TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this);}return value;}

remove方法

  1. 首先获取当前线程,并根据当前线程获取一个属于那个线程的Map
  2. 如果获取的Map不为空,则移除当前ThreadLocal对象对应的键值对entry,如果Map中不存在当前ThreadLocal对象对应的键值对entry,则不用管
/*删除当前线程中保存的ThreadLocal对应的实体entry(也就是键值对)
*/
public void remove() {// 获取当前线程对象中维护的ThreadLocalMap对象ThreadLocalMap m = getMap(Thread.currentThread());// 如果此map存在if (m != null) {// 存在则调用map.remove// 以当前ThreadLocal为key删除对应的实体entrym.remove(this);}}

在这里插入图片描述


存在的内存泄漏问题

Memory overflow:内存溢出,没有足够的内存提供申请者使用
Memory leak:内存泄漏是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。内存泄漏的堆积终将导致内存溢出。
在这里插入图片描述

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

Agilent 安捷伦 DSO91304A 高性能示波器

Agilent 安捷伦 DSO91304A 高性能示波器 DSO91304A Infiniium 高性能示波器&#xff1a;13 GHz 13 GHz4个模拟通道高达 1 Gpts 存储器和 40 GSa/s 采样率可以提供更完整的信号迹线捕获更低的本底噪声&#xff08;50 mV/格时为 1.73 mVrms&#xff09;和深入的抖动分析功能能够…

我国网络安全领域有哪些法律法规?主要内容是什么?

1. 背景介绍 网络信息安全方面的法规在全球范围内都有相应的立法&#xff0c;我们主要的立法有《网络安全法》、《密码法》、《数据安全法》以及《个人信息保护法》。当前也有一些相关的条例和管理办法&#xff0c;接下来就为大家一一介绍。 2. 法规介绍 在中国&#xff0c;…

Error in onLoad hook: “SyntaxError: Unexpected token u in JSON at position 0“

1.接收页面报错 Error in onLoad hook: "SyntaxError: Unexpected token u in JSON at position 0" Unexpected token u in JSON at position 0 at JSON.parse (<anonymous>) 2.发送页面 &#xff0c;JSON.stringify(item) &#xff0c;将对象转换为 JSO…

《昇思25天学习打卡营第14天|onereal》

第14天学习内容如下&#xff1a; Diffusion扩散模型 本文基于Hugging Face&#xff1a;The Annotated Diffusion Model一文翻译迁移而来&#xff0c;同时参考了由浅入深了解Diffusion Model一文。 本教程在Jupyter Notebook上成功运行。如您下载本文档为Python文件&#xff0c…

跨越界限的温柔坚守

跨越界限的温柔坚守 —— 郑乃馨与男友的甜蜜抉择在这个光怪陆离、瞬息万变的娱乐圈里&#xff0c;每一段恋情像是夜空中划过的流星&#xff0c;璀璨短暂。然而&#xff0c;当“郑乃馨与男友甜蜜约会”的消息再次跃入公众视野&#xff0c;它不仅仅是一段简单的爱情故事&#xf…

PageHelper分页查询遇到的小问题

如果我们是这样子直接查询 pagehelper会拼接导我们的sql语句之后 这样子我们搜索出来的list&#xff0c;就是里面参杂了PageHelper的东西 所以我们可以直接转成我们的Page类型 但是如果我们搜索出来的是List<Blog>&#xff0c;我有些信息不想返回给前端&#xff0c;所以…

【游戏客户端】大话版本slg玩法正式上线~~

【游戏客户端】制作率土之滨Like玩法 大家好&#xff0c;我是Lampard家杰~~ 好久好久没有更新博客了&#xff0c;有不少大佬都在后台私信我催更&#xff0c;但是很悲伤这段时间都忙的不行QAQ 那在忙什么呢&#xff1f;就是在制作一个SLG类的玩法【帮派纷争】啦 &#xff0c;布…

IT高手修炼手册(4)PowerShell命令

一、前言 PowerShell是一个功能强大的命令行界面和脚本环境&#xff0c;它允许用户管理Windows操作系统和应用程序。 二、文件和目录操作 Get-ChildItem&#xff1a;列出指定路径下的文件和文件夹。简写为ls或dir。 Copy-Item&#xff1a;复制文件和文件夹。简写为copy或cp。 M…

2024暑假集训

Day1——枚举 Day2——测试 Day3——贪心 Day4、5——测试 ——————————————————————————————————————————— Day3T7&Day5T7:没思路 Day3T8:不知道怎么排序筛选 Day5T5:没有算法难度&#xff0c;但是不知道怎么处理2队奶牛的情…

moviepy给视频添加字幕很慢的问题解决

前面说了如何通过moviepy给视频添加字幕&#xff1a; https://blog.csdn.net/qq_30594137/article/details/140094118?spm1001.2014.3001.5502 但是真实添加字幕的时候&#xff0c;就会很耗时&#xff0c;这是因为如下问题导致的。 CompositeVideoClip的入参是个list&#x…

ComfyUI+MuseV+MuseTalk图片数字人

电脑配置 GPU12G&#xff0c;如果自己电脑配置不够&#xff0c;选择云gpu&#xff0c;我就是用的这个&#xff0c;自己电脑太老配置跟不上 环境&#xff1a; Python 3.11.8 torch 2.2.1 cuda_12.1 资源提供&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1_idZbF…

基于单片机的 LED 花样照明时钟设计

摘要 &#xff1a; 本设计是基于单片机的 LED 花样照明 &#xff0c; 并附加时钟设计 . 单片机也叫微控制器 &#xff08; Micro Control Unit, 简称 MCU &#xff09;&#xff0c; 因其价格低廉 &#xff0c; 功能强大 &#xff0c; 在实际应用中得到广泛认可 . 本设计…

mac如何安装nvm

​ vue项目开发&#xff0c;热更新&#xff0c;webpack&#xff0c;前辈造的轮子&#xff1a;各类的工具&#xff0c;库&#xff0c;像axios,qs,cookie等轮子在npm上可以拿来直接用&#xff0c;需要node作为环境支撑。 开发时同时有好几个项目&#xff0c;每个项目的需求不同…

关于MCU-Cortex M7的存储结构(flash与SRAM)

MCU并没有DDR&#xff0c;所以他把代码存储在flash上&#xff0c;临时变量和栈运行在SRAM上。之所以这么做是因为MCU的CPU频率很低&#xff0c;一般低于500MHZ&#xff0c;flash的读取速度能够满足CPU的取指需求&#xff0c;但flash 的写入速度很慢&#xff0c;所以引入了SRAM …

VSCode神仙插件——CodeSnap (好看的代码截图)

1 安装 2 使用 选中要截图的代码,右键 此时右侧会出现代码截图的预览图 如果要将截图保存到本地,则点击上图红色框中的图标 也可以点击下面截的图,CtrlC复制,然后就可以CtrlV粘贴到其他应用程序里了

SpringBoot运维篇

工程打包与运行 windows系统 直接使用maven对项目进行打包 jar支持命令行启动需要依赖maven插件支持&#xff0c;打包时须确认是否具有SpringBoot对应的maven插件 <build><plugins><plugin><groupId>org.apache.maven.plugins</groupId><ar…

一篇就够了,为你答疑解惑:锂电池一阶模型-在线参数辨识(附代码)

锂电池一阶模型-在线参数辨识 背景在线 VS 离线 参数辨识递推最小二乘法一阶戴维南Z域离散表达式 背景 锂电池一阶戴维南等效模型的基础知识和离线辨识方法&#xff0c;已经在上一期非常详细地讲解了一轮&#xff08;上期文章请戳此处&#xff09;&#xff0c;本期继续讲解一下…

《昇思 25 天学习打卡营第 11 天 | ResNet50 图像分类 》

《昇思 25 天学习打卡营第 11 天 | ResNet50 图像分类 》 活动地址&#xff1a;https://xihe.mindspore.cn/events/mindspore-training-camp 签名&#xff1a;Sam9029 计算机视觉-图像分类&#xff0c;很感兴趣 且今日精神颇佳&#xff0c;一个字&#xff0c;学啊 上一节&…

C++笔试强训1

文章目录 一、单项选择题二、编程题1. 组队竞赛1. 删除公共字符 一、单项选择题 解析&#xff1a; for(初始化部分;条件判断部分;调整部分) { //循环部分 } 本题中条件判断部分是(y 123) && (x < 4)&#xff0c;中间用&&相连&#xff0c;左右都为真时才为真…

STM32实战项目:从零打造GPS蓝牙自行车码表,掌握传感器、蓝牙、Flash存储等核心技术

一、 引言 骑行&#xff0c;作为一项绿色健康的运动方式&#xff0c;越来越受到人们的喜爱。而记录骑行数据&#xff0c;分析速度、里程等信息&#xff0c;则成为了许多骑行爱好者的追求。本篇文章将带你使用STM32单片机&#xff0c;DIY一款功能完备的自行车码表&#xff0c;记…