[Java] 从内存的角度去理解ThreadLocal如何把不同线程间的访问隔离开来?ThreadLocal的内存泄露问题是什么?如何避免?

news2025/1/16 16:09:50

文章目录

  • 前言
  • 前置知识:堆内存与栈内存
  • 普通数据结构类和ThreadLocal存取数据的不同?
  • 结合源码来看ThreadLocal如何实现的
    • 1. ThreadLocal类get方法
    • 2. ThreadLocal类的getMap(Thread)方法
    • 3. Thread类的threadLocals属性
    • 4. ThreadLocal类的setInitialValue()方法
    • 5. 数据结构类 ThreadLocal\$ThreadLocalMap
    • 6. 键值对类 ThreadLocal\$ThreadLocalMap\$Entry
  • ThreadLocal的内存泄露问题
    • ThreadLocal如何导致内存泄露?
    • 如何正确使用ThreadLocal以避免内存泄露?
  • 结语

前言


在Java多线程编程里,线程之间访问共享区域(堆内存)里的数据时,通常面临需要加锁来保证线程安全性。有时候,我们各个线程其实只关心与之相关的数据,而把这些数据通过堆内存里某种数据结构(如Map、List等)存储时,这个数据结构实例就面临多线程增删改查的一个问题(需要加锁来保证线程安全或不加锁导致的线程不安全性)。

所以JDK提供了一个数据结构或者说机制,让我们能够很方便地不加锁也能实现每个线程单独访问其关联的数据,这就是咱们的ThreadLocal了。

前置知识:堆内存与栈内存


在了解ThreadLocal之前,我们首先需要知道堆内存与栈内存都分别意味着什么?

  • 堆内存(Heap memory):进程内的共享内存区域,被称为堆内存。各个不同线程都能轻易访问到。
  • 栈内存(Stack memory):进程内的各个线程独享的内存区域。在线程上指令的执行跳转和返回是通过栈帧(stack frame)在线程栈(stack)的压入和弹出来实现的,所以线程独享的内存区域也被称为栈内存。

我们能看到的图解呢,通常长下面这样,每个线程都有其单独的栈内存,整个进程里所有线程共享堆内存
进程的堆内存和栈内存

普通数据结构类和ThreadLocal存取数据的不同?


在前言中我们提到了通过普通数据结构类来存储共享数据的问题,那就是处于多线程环境下的普通数据结构类不是线程安全的,可能会导致数据出问题(比如java8之前多线程操作HashMap会导致死循环的问题)。要想保证线程安全性就得加锁,会损失一部分性能。

但是一些情况下我们其实并不希望加锁,我们仅仅希望有某一种机制能让不同线程的数据放在一起(一个变量里),但又彼此隔离(不需要加锁损失性能)。所以JDK中利用ThreadLocal类和Thread类相互配合实现了这一需求。

普通数据结构和ThreadLocal的不同可以看下图,左边部分是普通数据结构,右边部分是ThreadLocal。

  • 普通数据结构:所有线程指令需要访问同一个数据结构,数据结构内部需要通过加锁来实现线程安全,否则就是线程不安全的(如图示的HashMap就是线程不安全的)
  • ThreadLocal:ThreadLocal类仅仅作为一个访问器(Accessor)和令牌(Key),我们线程通过ThreadLocal去访问存储在Thread实例内部的ThreadLocalMap数据结构里的数据

请添加图片描述
不难看出,它们的区别就是普通数据结构就是一个数据结构实例来服务多个线程,
ThreadLocal这边呢则是每个线程都对应拥有一个自己的数据结构实例(ThreadLocalMap),ThreadLocal仅仅是作为一个访问器或者说代理的作用。

也正是每个线程都拥有自己的数据结构实例,这也是为什么不需要加锁或者说线程独占数据在Heap Memory上实现隔离的原因。

结合源码来看ThreadLocal如何实现的


通过前一章,我们了解到其实ThreadLocal的实现原理真的非常简单,下面我们通过结合源码的方式来看看ThreadLocal具体是怎么实现的

1. ThreadLocal类get方法

  1. 通过Thread.currentThread()获取到方法执行时的线程实例。
  2. 通过getMap(Thread)方法获取Thread实例的数据结构类ThreadLocalMap
  3. 如果数据结构存在,就尝试获取ThreadLocal实例这个Key对应的数据。获取到就直接返回。
  4. 如果数据结构不存在或者ThreadLocal这个Key不存在,就通过setInitialValue()方法初始化数据结构和添加初始值(null)

ThreadLocal类的get方法

2. ThreadLocal类的getMap(Thread)方法

这个方法就很简单了,直接获取Thread实例的threadLocals这个属性。

ThreadLocal类的getMap方法

3. Thread类的threadLocals属性

这个数据结构实例是被Thread实例持有的,这也是为什么说ThreadLocal和Thread一起提供了这个机制。
这个属性的初始化是典型的懒汉初始化(lazy initialization),初始值为null。

Thread类的threadLocals属性

4. ThreadLocal类的setInitialValue()方法

这个方法也很简单,如果数据结构ThreadLocalMap不存在就创建ThreadLocalMap实例并添加一个键值对。

  • Key: 当前ThreadLocal实例
  • Value: initialValue(),这个方法返回的是null。

ThreadLocal类的setInitialValue()方法

5. 数据结构类 ThreadLocal$ThreadLocalMap

这是一个类似HashMap的数据结构,你可以通过Key去获取对应的Value。只不过Key的类型被限定为ThreadLocal实例而已。

数据结构类 ThreadLocal$ThreadLocalMap

其键值对信息则是用一个Entry数组来保存。

在这里插入图片描述

6. 键值对类 ThreadLocal$ThreadLocalMap$Entry

和HashMap不同的是,ThreadLocalMap的键值对类Entry是WeakReference<ThreadLocal<?>>的子类,是弱引用,也是因为这个弱引用,引入了内存泄露的风险,这个我们会在后面章节讨论。

在这里插入图片描述

ThreadLocal的内存泄露问题


ThreadLocal如果使用不当会有内存泄露的风险,内存泄露,英:Memory Leak。内存泄露其实这个翻译笔者并不喜欢,而事实上进程的内存空间被操作系统严格管理,并不会像油箱泄漏那样漏得到处都是,所以这个听上去像是油箱漏油一般的翻译,并不便于我们中国人理解。

事实上,进程的内存中有一块因无法回收导致无法使用的区域存在这一现象被称为Memory Leak,Leak也有漏洞、裂缝的意思。内存裂缝就相对来说比较形象。我的内存像一块板子,上面裂了一块导致我们无法使用。

ThreadLocal如何导致内存泄露?

我们前面章简单看了一下ThreadLocal相关的源码以及简单介绍了内存泄漏时什么,下面我们来梳理一下ThreadLocal机制内的引用关系:

  1. ThreadLocalMap是Thread的属性,Thread实例强引用了 ThreadLocalMap实例。
  2. Entry[]是ThreadLocalMap的属性,ThreadLocalMap强引用Entry数组实例。
  3. Entry是Entry数组的成员,Entry数组强引用了Entry实例。
  4. Entry:
    4.1. Entry实例强引用了Object实例。
    4.2. Entry实例的父类WeakReference<ThreadLocal<?>>,弱引用了ThreadLocal实例

到4.2这里我们能看到Entry实例是弱引用的ThreadLocal实例,那么一旦ThreadLocal实例的强引用数量为0,那么ThreadLocal实例就会被回收。而我们的数据Entry.value对应的Object实例就会因为一条强引用链的存在导致无法被GC回收,导致出现内存泄漏。

Thread → ThreadLocalMap → Entry[] → Entry → Object

除非我们的Thread指令执行结束被回收,否则Object实例将永远不可能被回收。

如何正确使用ThreadLocal以避免内存泄露?

前面我们也提到了出现内存泄露的前提是ThreadLocal实例被回收,那么避免内存泄漏的方法就很容易想到了,那就是及时使用ThreadLocal的remove()方法清除不再使用的数据。这种方式会把ThreadLocal实例对于的Entry的Key和Value都设为null,避免了上述强引用链的存在。也就是在ThreadLocal被回收之前,我们需要主动手动清理不再使用的数据。

结语


虽然高级编程语言的出现让开发者不再需要关注进程的内存管理这块儿,但是一些类的不当使用依然有导致内存泄漏的风险。理解工具实现的原理以及内存和内存管理的本质,才能更好的编写出高质量稳定运行的程序。

我是虎猫,希望本文对你有所帮助。(=・ω・=)

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

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

相关文章

win11该文件没有与之关联的应用怎么办

win11用户在使用电脑的时候遇到了“该文件没有与之关联的应用”的提示&#xff0c;这是怎么回事呢&#xff1f;应该怎么办呢&#xff1f;出现这个情况应该是注册表被误删了&#xff0c;大家需要新建一个文本文档&#xff0c;然后输入下文提供的指令&#xff0c;之后将其重命名为…

亚信科技亮相南京软博会,数智赋能百行千业

11月23至25日&#xff0c;主题为“软件赋能 数智转型”的2022中国&#xff08;南京&#xff09;国际软件产品和信息服务交易博览会在南京国际博览中心盛大启幕。“数智化全栈能力提供商”亚信科技携“云网边端”产品体系&#xff0c;5G、人工智能、边缘AI、机器人流程自动化、数…

AlphaFold2源码解析(4)--模型架构

AlphaFold2源码解析(4)–模型架构 我们将Alphafold的流程分为一下几个部分&#xff1a; 搜索同源序列和模板特征构造特征表示MSA表示与残基对表示之间互相交换信息残基的抽象表示转换成具体的三维空间坐标 模型参数 AlphaFold有多个不同类型的参数&#xff08;单体&#x…

一文让你理解Linux权限问题

前言&#xff1a; 权限是个很重要的一部分&#xff0c;无论是在Linux系统中还是在生活里&#xff0c;权限都是必不可缺失的一部分&#xff0c;在生活中&#xff0c;权限是很常见的&#xff0c;例如VIP&#xff0c;如果你不是VIP你就不能享用VIP的一些特有的功能&#xff0c;这就…

WebRTC学习笔记四 RTCDataChannel

一、RTCDataChannel 简单来说&#xff0c;RTCDataChannel 就是在点对点连接中建立一个双向的数据通道&#xff0c;从而获得文本、文件等数据的点对点传输能力。它依赖于流控制传输协议&#xff08;SCTP&#xff09;&#xff0c;SCTP 是一种传输协议&#xff0c;类似于 TCP 和 U…

[ECCV2022]Language-Driven Artistic Style Transfer

标题&#xff1a;Language-Driven Artistic Style Transfer 链接&#xff1a;https://sites.cs.ucsb.edu/~william/papers/LDAST.pdf 如标题所示&#xff0c;本文做的是基于文本引导的风格迁移。整体的思路还是用的AST(arbitrary style transfer)那一套自编码器结构。AST的思…

期中考试【Verilog】

期中考试【Verilog】前言推荐期中考试一. 单选题&#xff08;共10题&#xff09;二. 填空题&#xff08;共5题&#xff09;三. 简答题&#xff08;共3题&#xff09;四. 其它&#xff08;共4题&#xff09;最后前言 编写于2022/11/30 13:30 以下内容源自Verilog期中试题 仅供…

Windows访问centOS的Tomcat

首先&#xff0c;先准备好jdk1.8和Tomcat的文件 点击此处获取jdk1.8和Tomcat的文件&#xff08;提取码&#xff1a;xxrc&#xff09; 配置IP地址 打开终端输入ifconfig&#xff0c;检查centOS的ip地址 根据要求&#xff0c;是要把ip地址最后一位改为自己的学号&#xff08;前…

手把手教你做智能合约开源|多文件合约开源|引用文件开源

本文手把手教你使用 区块链浏览器 验证智能合约的三种方式。 验证单一 Solidity 文件 在开始验证之前&#xff0c;我们需要首先部署智能合约。进入 Remix IDE&#xff0c;创建一个合约新文件。复制粘贴下面的代码&#xff1a; // SPDX-License-Identifier: MITpragma solidit…

夜曲编程Python体验课

目录 day1 编程中的“文本” 代码规范 打印数字 打印字符串 注释 总结思维导图 day2 变量与赋值 变量 常量 赋值 格式化输出 转义字符&#xff1a; 总结思维导图 day3 编程中的“数字” 整形 浮点型 运算符 四种常见的四则运算符&#xff08; - * / &…

【软件测试】测试人的我们,咋做一个如鱼得水的测试员?

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 一千个人&#xff0…

短视频创作,主动变现和被动变现方式详解,建议收藏反复阅读-下

同样先说纲要&#xff0c;有兴趣可以继续看下去&#xff0c;上一篇讨论了抖音变现的有三个建议&#xff0c;①变现标准低、②变现天花板高、③可主动变现。 这一篇的内容只要是抖音上被动和主动两类变现方式&#xff0c;涉及了直播打赏&#xff0c;广告接单&#xff0c;视频带货…

小啊呜产品读书笔记001:《邱岳的产品手记-15》第28讲 产品分析的套路(上):谁是利益相关者? 29讲产品分析的套路(中):解决什么问题?

小啊呜产品读书笔记001&#xff1a;《邱岳的产品手记-15》第28讲 产品分析的套路&#xff08;上&#xff09;&#xff1a;谁是利益相关者&#xff1f;& 29讲产品分析的套路&#xff08;中&#xff09;&#xff1a;解决什么问题&#xff1f; 一、今日阅读计划二、泛读&知…

【Big Data】Hadoop--MapReduce经典题型实战(单词统计+成绩排序+文档倒插序列+每月Top3温度)

&#x1f34a;本文使用了4个经典案例进行MapReduce实战 &#x1f34a;参考官方源码&#xff0c;代码风格较为优雅 &#x1f34a;解析详细 一、Introduction MapReduce是一个分布式运算程序的编程框架&#xff0c;核心功能是将用户写的业务逻辑代码和自身默认代码整合成一个完整…

vue+videojs视频播放、视频切换、视频断点分段上传

“本次需求是做一个视频列表&#xff0c;点击视频列表播放对应视频&#xff1b;同时要求实现断点分段上传大文件&#xff08;视频&#xff09;的功能 。 videojs文档&#xff1a;Getting Started with Video.js - Video.js: The Player Framework | Video.js 断点续传组件地址…

WebRTC学习笔记六 兼容性 adapter.js

一、adapter.js发展背景 adapter.js自2012年底或者2013年初WebRTC早期的时候就已经出现了。它最初是Google的apprtc demo的一部分。原始版本仍可在Chrome tree中找到。它是一个非常小的项目&#xff0c;还没有150行。主要功能是隐藏像webkitRTCPeerConnection和mozRTCPeerConne…

Spring Boot+Mybatis:实现数据库登录注册与两种properties配置参数读取

〇、参考资料 1、hutool介绍 https://blog.csdn.net/abst122/article/details/124091375 2、Spring BootMybatis实现登录注册 https://www.cnblogs.com/wiki918/p/16221758.html 3、Spring Boot读取自定义配置文件 https://www.yisu.com/zixun/366877.html 4、Spring Boot读取p…

医院用故障电弧探测器AAFD 安科瑞 时丽花

摘 要&#xff1a; 医院运行中对于用电方面的要求越来越高&#xff0c;为了更好地体现用电价值&#xff0c;首先应该确保用电的安全性&#xff0c;尤其是对 于越来越繁杂的医院用电系统。基于此&#xff0c;在未来医院用电过程中应该加大关注力度&#xff0c;切实做好相关管理工…

Compose学习-> Text()

设置文本&#xff1a;text xxx 直接设置 Text(text "我是一个Text")引用资源文件&#xff1a;stringResource Text(text stringResource(id R.string.string_text))设置字体颜色&#xff1a;color xxx 引用系统自带的颜色 Text(text "我是一个Text"…

【技术分享】NB860+Lierda云平台=上电即上云——云管端协作让万物互联更简单(二)

随着物联网行业的快速发展&#xff0c;越来越多的物联网云服务平台涌现。如何快速实现应用开发&#xff0c;如何管理&#xff0c;如何让设备快速上云&#xff0c;成为关注的焦点。 第一期中我们介绍了基于MQTT协议快速接入利尔达物联网全连接云平台&#xff0c;本期我们将介绍如…