详解Java ThreadLocal

个人博客

详解Java ThreadLocal | iwts’s blog

Java ThreadLocal

ThreadLocal提供了线程内存储变量的能力,这些变量不同之处在于每一个线程读取的变量是对应的互相独立的。通过get和set方法就可以得到当前线程对应的值。

TreadLocal存储模型

ThreadLocal的静态内部类ThreadLocalMap为每个Thread都维护了一个数组table,ThreadLocal确定了一个数组下标,而这个下标就是value存储的对应位置。
这样看太抽象了,具体数据结构可以从存储方法开始看。

根据set方法探究ThreadLocal存储模型

看上去简单粗暴,先是获取线程,然后看能不能获取数据结构,能获取就直接set,获取不到就初始化。

那么这里指向了一个核心方法:ThreadLocalMap是怎么获得的。

这个东西竟然是存储在线程对象里面的:

等于说TreadLocal的核心内部类,是存在一个线程中的。
这个时候再回过头来看ThreadLocalMap:

这个Entry继承了WeakReference,代表他是一个弱引用,而其泛型则是ThreadLocal,那么说明了单个Entry节点,底层是一个kv结构,key是ThreadLocal的弱引用,value是一个Object。

但是这个只是Entry节点,ThreadLocalMap的底层并不是KV结构,而是一个Entry数组。

这里再继续看set()方法是怎么处理的:

可以看出来,Entry的key就是这个具体的ThreadLocal对象,value是具体设置的value。

那么这个下标怎么计算的?

这里与计算比较类似HashMap,保证数组长度是2的幂,这样与运算直接就是取模了,具体内容参考下面。

综上,可以得到ThreadLocal究竟是怎么存的:

注意,key存储的是ThreadLocal的弱引用,那么扩展到JVM内存模型下:

ThreadLocal本身的引用是正常放在栈中的,但是由于WeakReference,所以key对ThreadLocal的引用是虚引用。这也是OOM的主要原因。

Thread中threadLocals

整个ThreadLocal的数据都是维护在单个线程的属性中,所以保证了线程间的隔离,因为这玩意就是线程自己的一个变量。

ThreadLocalMap

ThreadLocal的内部类,其中定义了Entry节点,存储具体的数据。而本身提供了Entry数组,用于存储线程下全部的ThreadLocal数据。

此外提供了一系列数组操作方法,以及扩容等操作。

Entry

内部类Entry,继承了WeakReference,其内部设置属性value,实现了一个类似KV结构的数据结构,保证了key是ThreadLocal的一个弱引用,value是具体的数值。

ThreadLocalMap的维护

getMap()

等于从线程中获取线程本身的ThreadLocalMap对象。

ThreadLocalMap的初始化

java.lang.ThreadLocal#createMap

直接走了构造方法:

初始化了Entry数组。初始化数组长度为16,并且设置了扩容阈值threshold。具体看下面。

扩容机制

类似HashMap扩容,初始化长度为16,后续扩容的时候直接*2,保证长度是2的幂。只有在超过阈值的时候才会扩容。

阈值threshold的维护,为当前数组长度的2/3。

跟HashMap一样,扩容后需要对旧值重新hash,定位到确定的下标。相比HashMap简单多了。

ThreadLocal对象hash定位下标

算法看起来还是比较简单的。了解HashMap这里没啥难度,由于长度必然是2的幂,这样与计算也是和取模的效果是一样的。

主要是这个threadLocalHashCode是什么。

这里算是一个小优化了,主要获取方法:

这里走CAS执行一次自增,增量为常量0x61c88647。

这样,0x61c88647是斐波那契散列乘数,那么后续的自增导致hash出来的结果分布会比较均匀,可以很大程度上避免hash冲突,下面是15次操作获取的hash值

hash冲突

由于ThreadLocalMap的底层是一个数组,设计上也不想太复杂,所以没有采用HashMap的拉链法,而是采用线性探测来解决hash冲突。

非常简单,其实就是+1,看是否越界,越界设置0。其实这里改成取模更加简洁。

ThreadLocal弱引用与内存泄漏

回到最上面Entry的设计,继承了WeakReference,表明了key的对象是个弱引用。

那么在ThreadLocal执行完毕后,如果没有及时清除,就会导致ThreadLocal对象没有具体的引用对象。那么就会被GC回收掉。

参考上面的图,GC回收的是堆,堆中的这个key,他引用的是ThreadLocal,也就是说这个key的引用,是会被GC回收掉的。

而Entry中value是个普通的Object,那么value本身是不会被回收的。
这样在大量操作后,就会导致value无法回收,导致内存泄漏。

解决方案

ThreadLocal使用结束后及时remove()。

而JDK为了解决这个问题,本身也或多或少在帮助我们回收。

remove()的时候会主动触发,而get()、set()在执行的时候也会间接执行:expungeStaleEntry()方法。

这个方法会主动清除所有Entry中key为null的对象。

强引用解决方案

比较奇葩,感觉用的不多。

即利用static修饰ThreadLocal,这样就能保证ThreadLocal对象是强引用,从根本上解决问题。

为什么选择用弱引用

依然是OOM问题。

由于ThreadLocalMap的生命周期跟Thread一样长,如果是强引用,那么手动删除对应key的情况下,仍然会导致内存泄漏。

但是使用弱引用可以多一层保障:弱引用ThreadLocal被清理后key为null,对应的value在下一次ThreadLocalMap调用set、get、remove的时候可能会被清除。

弱引用虽然会导致OOM,但是强引用不仅会导致OOM,还会更多。

ThreadLocal API

都比较简单感觉没什么好聊的。

需要注意的是hash冲突问题,由于hash冲突的解决方案是线性探测,所以在get的时候,需要对比key是否一致,如果不一致可能是hash冲突,需要利用线性探测,循环遍历一轮数组。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Oracle EBS API创建AP发票报错:ZX_TAX_STATUS_NOT_EFFECTIVE和ZX_REGIME_NOT_EFF_IN_SUBSCR-

背景 由创建国外业务实体财务未能提供具体国家地区会计税制,而是实施人员随便选择其它国外国家地区会计税制。导致客户化创建AP发票程序报错:UNEXPECTED TAX ERROR-导入时出现意外的税务错误ZX_TAX_STATUS_NOT_EFFECTIVE-ZX_REGIME_NOT_EFF_IN_SUBSCR-ZX…

如何克隆非默认分支

直接git clone下来的我们知道是默认分支,那如何克隆其他分支呢: 比如这个,我们想克隆AdvNet。 我们可以在本地文件夹打开Git Bash 依次输入: git clone --branch AdvNet https://github.com/wgcban/SemiCD.git cd SemiCD git b…

计算机操作系统体系结构

我是荔园微风,作为一名在IT界整整25年的老兵,今天给大家讲讲操作系统。 当今的操作系统趋向于越来越复杂,因为它们提供许多服务,并支持各种硬件和软件资源(请参见“操作系统思想:尽量保持简单”&#xff0…

267 基于matlab的信号处理GUI人机交互

基于matlab的信号处理GUI人机交互,利用GUI功能完成包括振幅调制AM(Amplitude Modulation),双边带调幅信号DSB(double sideband),单边带信号SSB(single sideband )&#x…

《TCP/IP网络编程》(第十一章)进程间通信

进程间通信意味着两个不同的进程间可以交换数据,它使得不同的进程能够协同工作,实现复杂的系统功能。 1.通过管道实现进程间通信 下图是基于 管道(PIPE) 的进程间通信结构模型 管道不属于进程的资源,属于操作系统的资…

数字化工厂怎么收集,处理数据?

数字化工厂的数据收集与处理 数字化工厂是现代化工厂,利用数字技术和数据分析提高效率和优化流程。数据分析作为数字化工厂的核心技术,对数据的获取与处理至关重要。在数字化工厂中,数据的来源包括企业内部信息系统、物联网信息以及外部信息&…

美光EMMC芯片丝印型号查询 8LK17/D9PSK, OXA17/JY997

问题说明 最近在使用美光EMMC的时候,发现通过芯片丝印查询不到 芯片的规格说明书; 经过查阅资料,发现美光的EMMC芯片 “由于空间限制,FBGA 封装组件具有与部件号不同的缩写部件标记”,需要通过官网查询丝印的FBGA cod…

Autosar Dcm配置-特定NRC实现方式-基于ETAS软件

文章目录 前言工具配置代码编写总结 前言 项目开发过程中,诊断服务一般客户需求或系统需求都会有特定NRC(一般为NRC22-条件不满足),也就会有特定的条件,需要手动加代码实现。本文介绍ETAS工具中配置的接口及简单实现。 工具配置 对于每一个…

特朗普竞选带火PoliFi,以Bitget为例

以特朗普系列Meme币为代表的政治金融(PoliFi)概念币市场正在掀起热潮,前美国总统特朗普(Donald Trump)在本月稍早公开力挺加密货币,接着又在周二宣布接受比特币、以太币、SOL、USDC、DOGE…等政治献金,让相关通证高涨。 据CoinGecko数据&…

鸿蒙开发接口图形图像:【@ohos.display (屏幕属性)】

屏幕属性 屏幕属性提供管理显示设备的一些基础能力,包括获取默认显示设备的信息,获取所有显示设备的信息以及监听显示设备的插拔行为。 说明: 开发前请熟悉鸿蒙开发指导文档: gitee.com/li-shizhen-skin/harmony-os/blob/master/…

【C++】继承(二)深入理解继承:派生类默认成员函数与友元、静态成员的奥秘

目录 派生类的默认成员函数①派生类的构造函数②派生类的拷贝构造函数③派生类的赋值构造④派生类的析构函数 继承与友元继承与静态成员 前言 我们在上一章讲解了: 继承三部曲,本篇基于上次的基础继续深入了解继承的相关知识,欢迎大家和我一起学习继承 派…

探索Python中的随机数生成与统计分析

新书上架~👇全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我👆,收藏下次不迷路┗|`O′|┛ 嗷~~ 目录 一、随机数的魅力与实用性 1. 随机数生成基础 2. 批量生成随机数 二、随机数的高级应用&a…

windows中每日定时执行python脚本,解决问题

由于需要一个每天定时执行的任务,所以需要定时启动,网上看了很多方法,感觉不能在python脚本种写个while true 定时执行,占资源不说还不可靠。 最后考虑通过系统工具定时启动,发现linux中有crontab,windows…

前端 CSS 经典:水波进度样式

前言&#xff1a;简单实现水波进度样式&#xff0c;简单好看。 效果图&#xff1a; 代码实现&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"utf-8" /><meta http-equiv"X-UA-Compatible" cont…

git:Unable to negotiate问题解决

场景说明&#xff1a; 安装了Gitblit(自架的代码仓库服务)发现部分电脑无法推代码&#xff0c;报错误如下&#xff1a; Unable to negotiate with **** port 22: no matching host key type found. Their offer: ssh-rsa 并排队了账户权限问题。 解决方案&#xff1a; 1.打开问…

5.28 学习总结

一.CSS学习(一) 一、CSS简介 1、什么是CSS CSS&#xff1a;Cascading Style Sheet 层叠样式表是一组样式设置的规则&#xff0c;用于控制页面的外观样式 2、为什么使用CSS 实现内容与样式的分离&#xff0c;便于团队开发样式复用&#xff0c;便于网站的后期维护页面的精确…

iptables练习题

目录 练习题1. 显示当前的iptables规则2. 允许所有来自192.168.1.0/24的TCP流量到本机的22端口&#xff08;SSH&#xff09;3. 禁止所有来自10.0.0.0/8的ICMP流量4. 允许所有出站流量5. 拒绝所有来自外部的HTTP流量&#xff08;80端口&#xff0c;tcp协议&#xff09;6. 删除IN…

数据恢复与取证软件: WinHex 与 X-Ways Forensics 不同许可证功能区别

天津鸿萌科贸发展有限公司从事数据安全业务20余年&#xff0c;在数据恢复、数据取证、数据备份等领域有丰富的案例经验、专业技术及良好的行业口碑。同时&#xff0c;公司面向取证机构及数据恢复公司&#xff0c;提供数据恢复实验室建设方案&#xff0c;包含数据恢复硬件设备及…

期货学习笔记-斐波那契学习1

斐波那契数列介绍 斐波那契数列是1、1、2、3、5、8、13、21、34、55、89…据说这是数学家莱昂纳多 斐波那契研究兔子繁殖时发现的一个神奇数列&#xff0c;似乎大自然在按照这个数列进行演化&#xff0c;一个斐波那契数字是由该数列相邻的前两个数字相加得到的 在斐波那契交易…

基于STM32实现智能交通灯控制系统

目录 引言环境准备智能交通灯控制系统基础代码示例&#xff1a;实现智能交通灯控制系统 GPIO控制交通灯定时器配置与使用红外传感器检测车辆用户界面与显示应用场景&#xff1a;城市交通管理与自动化控制问题解决方案与优化收尾与总结 1. 引言 本教程将详细介绍如何在STM32嵌…