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

ThreadLocal的设计思考

问题的提出

  • 在Java多线程中,共享变量的读写非常容易出现不可预测的行为,因此对共享变量的访问控制非常重要。因此在多线程编程时,为了保证线程安全,需要进行额外的同步措施。比如典型的操作就是加锁。除了加锁外,另一种常用的方法是使用ThreadLocal , 这种用法在框架中非常普遍。
  • ThreadLocal可以理解为线程本地变量。多个线程对同一个变量如何不互相影响,那么只有对自身独有的变量访问(独占内存)时才是线程安全的,因此,ThreadLocal 为变量在每个线程中都创建了一个副本,该副本只能被当前线程访问,多线程之间是隔离的,变量不能在多线程之间共享。这样每个线程修改变量副本时,不会对其他线程产生影响。

常规做法

  • 多线程访问 ThreadLocal 变量时都会有自己独立的实例副本,要维护线程与变量的对应关系,一种普遍的思维就是在 ThreadLocal 中维护一个映射表 Map 用于记录线程与实例之间的映射关系,也就是有一层总控的协调在里面。
  • 但是,如果存在总控进行协调,那么在新增线程和销毁线程时都需要更新 Map 中的映射关系时,就会在总控这里产生多线程并发修改,那么就又产生了额外的操作来保证 Map 是线程安全的。如果是在高并发的场景并发修改 Map 需要加锁,会明显降低性能。

JDK的实现

  • JDK的 ThreadLocal 提供了一种新的思路,从 Thread 的视角来看,在 Thread 中维护一个 Map用于记录 ThreadLocal 与实例之间的映射关系,这样在同一个线程内,Map 就不需要加锁了。

  • 从 Thread 代码定义看出:Thread类依赖了ThreadLocal.ThreadLocalMap;

public class Thread implements Runnable {/* ThreadLocal values pertaining to this thread. This map is maintained* by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;
} 
  • 从 ThreadLocal 的代码来看:
    1. 它的get/set都是从线程取到对应的ThreadLocal.ThreadLocalMap;代码仅以get示例。
    2. 它定义的ThreadLocalMap的 Entry 继承了WeakReference,Entry的key是弱引用,value是强引用。这样设计获得了另外一个好处:防止内存泄漏。在 JVM 垃圾回收时,只要发现了弱引用的对象,不管内存是否充足,都会被回收。如果key是强引用,当ThreadLocal不再使用时,ThreadLocalMap中还是存在对ThreadLocal的强引用,那么GC是无法回收的,从而造成内存泄漏。
public class ThreadLocal<T> {/*** Returns the value in the current thread's copy of this* thread-local variable.  If the variable has no value for the* current thread, it is first initialized to the value returned* by an invocation of the {@link #initialValue} method.** @return the current thread's value of this thread-local*/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();}ThreadLocalMap getMap(Thread t) {return t.threadLocals;}static class ThreadLocalMap {/*** The entries in this hash map extend WeakReference, using* its main ref field as the key (which is always a* ThreadLocal object).  Note that null keys (i.e. entry.get()* == null) mean that the key is no longer referenced, so the* entry can be expunged from table.  Such entries are referred to* as "stale entries" in the code that follows.*/static class Entry extends WeakReference<ThreadLocal<?>> {/** The value associated with this ThreadLocal. */Object value;Entry(ThreadLocal<?> k, Object v) {super(k);value = v;}}}
}
  • Entry的key设计成了弱引用,但是当ThreadLocal不再使用被 GC 回收后,ThreadLocalMap 中可能出现 Entry的key为null,那么Entry的value一直会强引用数据而得不到释放,只能等待线程销毁。
  • 如何避免ThreadLocalMap内存泄漏? ThreadLocal做了如下的保护措施,在执行 ThreadLocal.set()/get()方法时,ThreadLocal会调用expungeStaleEntry方法清除 ThreadLocalMap 中key为null的 Entry 对象,让它还能够被 GC 回收。
    • expungeStaleEntry方法会遍历ThreadLocalMap的散列表,从当前节点开始向后遍历数组,将过期的条目(即key为null的条目)清理掉,并将这些条目的位置设置为null。如果遇到未过期的数据,则重新计算其位置并重新分配,确保数据尽可能靠近正确的位置,以减少冲突和查找时间‌。

编程习惯

在编程时,还需要注意:

  • 当线程中某个ThreadLocal对象不再使用时,立即调用remove()方法删除Entry对象。
  • 如果是在异常的场景中,应在finally代码块中进行清理,保持良好的异常处理意识。

学得经验

  • 除了集中控制并发外,可以将竞争的数据分散出去。
  • 解决方案的自洽性,增加了线程自身的数据副本,需要保证生命周期的一致性,这里的设计尽最大可能的保证了GC的可执行。

相关文章:

ThreadLocal的设计思考

问题的提出 在Java多线程中&#xff0c;共享变量的读写非常容易出现不可预测的行为&#xff0c;因此对共享变量的访问控制非常重要。因此在多线程编程时&#xff0c;为了保证线程安全&#xff0c;需要进行额外的同步措施。比如典型的操作就是加锁。除了加锁外&#xff0c;另一…...

shell脚本练习(2)

1. 使用case实现成绩优良差的判断 2. for创建20用户 用户前缀由用户输入 用户初始密码由用户输入 例如&#xff1a;test01,test10 3. for ping测试指网段的主机 网段由用户输入&#xff0c;例如用户输入192.168.2 &#xff0c;则ping 192.168.2.10 --- 192.168.2.2…...

通讯专题4.1——CAN通信之计算机网络与现场总线

从通讯专题4开始&#xff0c;来学习CAN总线的内容。 为了更好的学习CAN&#xff0c;先从计算机网络与现场总线开始了解。 1 计算机网络体系的结构 在我们生活当中&#xff0c;有许多的网络&#xff0c;如交通网&#xff08;铁路、公路等&#xff09;、通信网&#xff08;电信、…...

Harmony NEXT-越过相机读写权限上传图片至项目云存储中

问题成因 在制作用户注册登录界面时想要实现用户头像上传共能&#xff0c;查询API文档&#xff0c;发现有picker和PhotoAccessHelper两个包可以选择使用&#xff0c;但是在使用PhotoAccessHelper包拉起相册并读入所选的照片后将该照片传入云存储中产生报错&#xff0c;需要相册…...

MATLAB基础应用精讲-【数模应用】Retinex图像去雾算法(附MATLAB和python代码实现)

目录 前言 算法原理 图像去雾 数学模型 算法步骤 算法拓展 多尺度Retinex (MSR) 算法 MSR算法的实现细节 McCann Retinex 算法 McCann99 Retinex算法 基于暗通道先验的图像去雾算法 暴力解法——直方图均衡化去雾 基于Retinex理论的图像去雾 基于暗通道先验的单…...

点击A组件跳转到B页面的tab的某一列

1、使用vuex存储点击的数据&#xff1b; 点击A组件里面的button按钮&#xff1a; <div><button click"banli(first)">已办理</button><button click"banli(second)">未办理</button><button click"banli(third)&quo…...

HarmonyOS xml转换JavaScript 常用的几个方法

HarmonyOS 使用 xml转换JavaScript 的好处 易用性&#xff1a; 提供了简洁的API接口&#xff0c;使得XML到JavaScript对象的转换变得简单直接。转换选项的灵活性允许开发者根据实际需求自定义转换结果。 高效性&#xff1a; HarmonyOS对底层运行时环境进行了优化&#xff0c;使…...

Linux笔记---进程:进程等待

1. 进程等待的概念 进程等待是指父进程通过系统调用wait或waitpid来对子进程进行状态检测与回收的功能。 当子进程退出时&#xff0c;如果父进程不读取子进程的退出状态&#xff0c;子进程就会成为僵尸进程&#xff0c;造成内存泄漏的问题。因此&#xff0c;父进程需要调用wa…...

【Linux】匿名管道通信场景——进程池

&#x1f525; 个人主页&#xff1a;大耳朵土土垚 &#x1f525; 所属专栏&#xff1a;Linux系统编程 这里将会不定期更新有关Linux的内容&#xff0c;欢迎大家点赞&#xff0c;收藏&#xff0c;评论&#x1f973;&#x1f973;&#x1f389;&#x1f389;&#x1f389; 文章目…...

算法妙妙屋-------1.递归的深邃回响:全排列的奇妙组合

全排列的简要总结 全排列&#xff08;Permutation&#xff09;是数学中一个经典的问题&#xff0c;指的是从一组元素中&#xff0c;将所有元素按任意顺序排列形成的所有可能序列。 特点 输入条件&#xff1a; 给定一组互异的元素&#xff08;通常为数组或字符串&#xff09;。…...

【maven-6】Maven 生命周期相关命令演示

Maven 是一个广泛使用的项目管理工具&#xff0c;尤其在 Java 项目中。它通过定义一系列的生命周期阶段&#xff08;Phases&#xff09;来管理项目的构建过程。理解这些生命周期阶段及其相关命令&#xff0c;对于高效地构建和管理项目至关重要。本文将通过实际演示&#xff0c;…...

黑马程序员Java笔记整理(day06)

1.继承的特点 2.继承的权限 3. 4.小结 5.方法重写 6.子类构造器 7.兄弟构造器 8.多态 9.小结...

LeetCode【代码随想录】刷题(动态规划篇)

509. 斐波那契数 力扣题目链接 题目&#xff1a;斐波那契数&#xff08;通常用F(n)表示&#xff09;形成的序列称为斐波那契数列 。该数列由0和1开始&#xff0c;后面的每一项数字都是前面两项数字的和。也就是&#xff1a; F(0) 0&#xff0c;F(1) 1 F(n) F(n - 1) F(n…...

【看海的算法日记✨优选篇✨】第三回:二分之妙,寻径中道

&#x1f3ac; 个人主页&#xff1a;谁在夜里看海. &#x1f4d6; 个人专栏&#xff1a;《C系列》《Linux系列》《算法系列》 ⛰️ 一念既出&#xff0c;万山无阻 目录 &#x1f4d6;一、算法思想 细节问题 &#x1f4da;左右临界 &#x1f4da;中点选择 &#x1f4da;…...

基于yolov8、yolov5的铝材缺陷检测识别系统(含UI界面、训练好的模型、Python代码、数据集)

摘要&#xff1a;铝材缺陷检测在现代工业生产和质量管理中具有重要意义&#xff0c;不仅能帮助企业实时监控铝材质量&#xff0c;还为智能化生产系统提供了可靠的数据支撑。本文介绍了一款基于YOLOv8、YOLOv5等深度学习框架的铝材缺陷检测模型&#xff0c;该模型使用了大量包含…...

计算机光电成像理论基础

一、透过散射介质成像 1.1 光在散射介质中传输 光子携带物体信息并进行成像的过程是一个涉及光与物质相互作用的物理现象。这个过程可以分为几个步骤来理解&#xff1a; 1. **光的发射或反射**&#xff1a; - 自然界中的物体可以发射光&#xff08;如太阳&#xff09;&am…...

【QNX+Android虚拟化方案】125 - 如何创建android-spare镜像

【QNX+Android虚拟化方案】125 - 如何创建android-spare镜像 1. Android侧创建 (ext4 / sparse) test_img.img 镜像 方法一2. Android侧创建 (ext4 / sparse) test_img.img 镜像 方法二3. qnx 侧 分区透传Android 配置3.1 配置分区透传3.2 Android 侧分区 rename3.3 创建挂载目…...

深度学习基础小结_项目实战:手机价格预测

目录 库函数导入 一、构建数据集 二、构建分类网络模型 三、编写训练函数 四、编写评估函数 五、网络性能调优 鲍勃开了自己的手机公司。他想与苹果、三星等大公司展开硬仗。 他不知道如何估算自己公司生产的手机的价格。在这个竞争激烈的手机市场&#xff0c;你不能简单地…...

EMall实践DDD模拟电商系统总结

目录 一、事件风暴 二、系统用例 三、领域上下文 四、架构设计 &#xff08;一&#xff09;六边形架构 &#xff08;二&#xff09;系统分层 五、系统实现 &#xff08;一&#xff09;项目结构 &#xff08;二&#xff09;提交订单功能实现 &#xff08;三&#xff0…...

【随笔】AI技术在电商中的应用

这几年&#xff0c;伴随着ChatGPT开始的AI浪潮席卷全球&#xff0c;从聊天场景逐步向多场景扩散&#xff0c;形成了广泛开花的现象。至今&#xff0c;虽然在部分场景的进展已经略显疲态&#xff0c;但当前的这种趋势仍然还在不断的扩展。不少公司&#xff0c;甚至有不少大型电商…...

序列式容器详细攻略(vector、list)C++

vector std::vector 是 STL 提供的 内存连续的、可变长度 的数组(亦称列表)数据结构。能够提供线性复杂度的插入和删除,以及常数复杂度的随机访问。 为什么要使用 vector 作为 OIer,对程序效率的追求远比对工程级别的稳定性要高得多,而 vector 由于其对内存的动态处理,…...

快速启动项目

1 后端项目 https://gitee.com/liuyunkai666/gungun-boot.git 分支&#xff1a; mini 是 springboot3 jdk17 的基础版本&#xff0c;后续其他功能模块陆续在其基础上追加即可​。 1.1 必备环境 1.1.1 mysql 创建一个 自定义名称 数据库&#xff0c;【只要】 执行对应数据库…...

springboot347基于web的铁路订票管理系统(论文+源码)_kaic

摘 要 当今社会进入了科技进步、经济社会快速发展的新时代。计算机技术对经济社会发展和人民生活改善的影响也日益突出&#xff0c;人类的生存和思考方式也产生了变化。传统铁路订票管理采取了人工的管理方法&#xff0c;但这种管理方法存在着许多弊端&#xff0c;比如效率低…...

使用API管理Dynadot域名,在账户中添加域名服务器(Name Server)

前言 Dynadot是通过ICANN认证的域名注册商&#xff0c;自2002年成立以来&#xff0c;服务于全球108个国家和地区的客户&#xff0c;为数以万计的客户提供简洁&#xff0c;优惠&#xff0c;安全的域名注册以及管理服务。 Dynadot平台操作教程索引&#xff08;包括域名邮箱&…...

【Linux | 计网】TCP协议深度解析:从连接管理到流量控制与滑动窗口

目录 前言&#xff1a; 1、三次握手和四次挥手的联系&#xff1a; 为什么挥手必须要将ACK和FIN分开呢&#xff1f; 2.理解 CLOSE_WAIT 状态 CLOSE_WAIT状态的特点 3.FIN_WAIT状态讲解 3.1、FIN_WAIT_1状态 3.2、FIN_WAIT_2状态 3.3、FIN_WAIT状态的作用与意义 4.理解…...

go语言的成神之路-筑基篇-对文件的操作

目录 一、对文件的读写 Reader 接口 Writer接口 copy接口 bufio的使用 ioutil库 二、cat命令 三、包 1. 包的声明 2. 导入包 3. 包的可见性 4. 包的初始化 5. 标准库包 6. 第三方包 7. 包的组织 8. 包的别名 9. 包的路径 10. 包的版本管理 四、go mod 1. 初始…...

两道数据结构编程题

1.写出在顺序存储结构下将线性表逆转的算法&#xff0c;要求使用最少的附加空间。 解&#xff1a;输入&#xff1a;长度为n的线性表数组A(1:n) 输出&#xff1a;逆转后的长度为n的线性表数组A(1:n)。 C语言描述如下&#xff08;其中ET为数据元素的类型&#xff09;&#xff1a;…...

【Qt】QDateTimeEdit控件实现清空(不保留默认时间/最小时间)

一、QDateTimeEdit控件 QDateTimeEdit 提供了一个用于编辑日期和时间的控件。用户可以通过键盘或使用上下箭头键来增加或减少日期和时间值。日期和时间的显示格式根据设置的格式显示&#xff0c;可以通过 setDisplayFormat() 方法来设置。 二、如何清空 我在使用的时候&#…...

12、字符串

1、字符串概念 字符串用来存储一组字符&#xff0c;因此需要字符数组来存。 C语言中字符串的表示 C语言里面字符串只能用字符数组来存 字符串要求这个数组的末尾必须至少有一个\0 char ch1[] {a,b,c}; // 不是字符串 char ch2[5] {h,e,l,l,o}; // 不是字符串 char…...

DPDK用户态协议栈-Tcp Posix API 1

和udp一样&#xff0c;我们需要实现和系统调用一样的接口来实现我们的tcp server。先来看看我们之前写的unix_tcp使用了哪些接口&#xff0c;这边我加上两个系统调用&#xff0c;分别是接收数据和发送数据。 #include <stdio.h> #include <arpa/inet.h> #include …...

简单网站开发/网站自动提交收录

一 概念 缓存的分类&#xff1a; 客户端缓存 &#xff08;Client Caching&#xff09; 代理缓存 &#xff08;Proxy Caching&#xff09; 反向代理缓存 &#xff08;Reverse Proxy Caching&#xff09; 服务器缓存&#xff08;Web Server Caching&#xff09; 使用缓存的好处&…...

手机网站 宽度/台州网站制作维护

传送门&#xff1a;poj 1077 Eight 题目大意 输入的八数码 将一个八数码最后转换为 1 2 3 4 5 6 7 8 x 的格式&#xff0c;然后打印出路径 康拓展开 如果按照平常的思路&#xff0c;把x的位置看做0&#xff0c;一共有8&#xff01;个状态&#xff0c;来判断某一个状态…...

网站开发的需求分析教学视频/seo推广宣传

文章目录前言前言 输入法右键&#xff0c;选择高级设置 选择自定义短语 设置单词缩写触发自定义短语 短语使用获得日期的代码即可 #$(year)年$(month_mm)月$(day_dd)日 星期$(weekday_cn) $(fullhour):$(minute):$(second)此格式为 2022年02月24日 星期四 18:29:07 可以根…...

武汉网站制作迈佳/免费好用的crm软件

最近购物节一波接一波&#xff0c;往常家里的孩子总嚷嚷着要买玩具&#xff0c;但这段时间却吵着要买球&#xff1f;到底是咋了&#xff1f;原来&#xff0c;最近有一档节目风靡全网&#xff0c;尤其是孩子们都成了“忠实粉丝”&#xff0c;收视率期期第一&#xff0c;它就是金…...

哪个网站可以做公众号封面/百度一下你就知道搜索引擎

第五章 原型模式——JavaScript的灵魂语言中的原型原型继承实例不使用原型实现继承原型模式原型继承什么时候用原型继承谈谈“原型继承的实现不需要了解创建的过程”的理解语言中的原型 JavaScript中的继承是靠原型链实现的&#xff0c;而原型模式就是将原型对象指向创建对象的…...

soho外贸网站建设/优化seo是什么意思

文章目录题目题目解析解题代码我的个人小站&#xff1a; acking-you.github.io题目 OJ平台 题目解析 实际上就是一个前缀和二分的处理&#xff0c;我一旦爆出前缀和二分&#xff0c;应该就都有思路了&#xff01; 解题代码 这里偷懒使用了STL&#xff0c;当然也可自己去写二…...