ThreadLocal从变量副本的角度解决多线程并发安全问题

2023-11-04

ThreadLocal从变量副本的角度解决多线程并发安全问题

之前我们讲的高并发场景下的线程安全问题,可以使用Synchronized同步关键字、Lock手动加锁的方式去解决,什么轻量级锁、偏向锁、重量级锁、可重入锁等等,实际上本质都是控制线程,使得多个线程同步的去访问共享资源。之所以多线程存在线程安全问题,就是因为多个线程访问同一个共享资源导致的,多个线程之间属于竞争关系,很容易就会导致数据的不安全。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Nh9HtNqq-1633258255949)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003044313949.png)]

我们说了加锁实际上保证了各个线程之间同步、有序的去访问共享资源,难道不加锁就没有办法解决多线程安全问题了吗?我们要抓主要矛盾,之所以存在并发安全问题,是因为共享资源只有一个,多线程会竞争获取共享资源,如果同一类共享资源有多个,或者说有多个副本给每一个线程使用呢?这样不就不用加锁了,每一个线程只需要存在自己的那个数据的副本即可,因此也就不存在资源竞争问题,也就保证了多线程下数据的安全。而ThreadLocal类就是给每个线程绑定了变量的本地副本,从而避免线程安全。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EKDJz1YS-1633258255952)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003044351993.png)]

接下来我们从源码来看看ThreadLocal类是如何给每一个线程保存变量的本地副本的。

首先我们看看Thread线程类,线程类里面有一个ThreadLocalMap类型的成员变量threadLocals,用来存放当前线程的本地变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9paSKNhs-1633258255955)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003044849554.png)]

实际上ThreadLocal类有一个ThreadLocalMap内部类,这个内部类你可以认为是一个专门用来维护线程本地变量HashMap集合

ThreadLocalMap这个类的数据结构是一个Entry类型的数组,用来保存一个个的Entry节点,Entry节点封装了以ThreadLocal实例对象为keyObject对象为value的键值对,保存在ThreadLocalMap

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hXN7z5v5-1633258255957)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003041216097.png)]

ThreadLocal类实际上是对ThreadLocalMap这个内部类的封装本地变量值最终是存放在ThreadLocalMap中的ThreadLocal类提供了set()get()等其他方法,来操作ThreadLocalMap中保存的数据。

ThreadLocal调用set()方法保存本地变量时,首先获取到当前线程,然后调用getMap()方法得到当前线程的ThreadLocalMap,底层实际上是调用ThreadLocalMapset()方法向这个Map集合中保存数据的在这里插入图片描述

当第一次调用set()方法时,会先调用createMap()方法创建出ThreadLocalMap对象,因此是懒加载的
在这里插入图片描述

接着会使用构造方法创建出ThreadLocalMap对象

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kiX42MxN-1633258255966)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003045634766.png)]

我们之前说过ThreadLocalMap是一个HashMap集合,因此也有初始容量、加载因子、阈值、散列函数、hashcode值。

ThreadLocalMap的初始容量默认为16,阈值为容量的2/3,利用ThreadLocalhashcode值,对容量进行取模运算,计算出Entry数组中对应的索引位置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5EVlC2k2-1633258255968)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003050738848.png)]
在这里插入图片描述

如果ThreadLocalMap之前已经创建出来了,就会调用set()方法向ThreadLocalMap中添加元素。根据hashcode值计算出数组中对应的索引位置,然后遍历这个map所有的Entry,如果key存在了就进行替换,没有的话就将键值对保存到ThreadLocalMap中。同时在遍历的过程中发现key为null,就会清除掉这个数据,并将新的数据存放到这个索引位置。这主要是释放了内存空间,防止内存泄漏

如果既没有发生替换,也没有发生可以清除掉的key,那么就会创建一个Entry,保存到计算出的对应的索引位置。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BcCDm9C9-1633258255973)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003174955938.png)]

我们说过ThreadLocalMap初始容量默认为16,阈值默认为容量的2/3。在向map中添加完数据时,最后还会进行一次清理工作,如果清理后发现当前map的大小还是大于等于阈值,就会触发扩容机制

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CH22bhiQ-1633258255974)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003173537503.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9oRcu5y4-1633258255976)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003175513153.png)]

ThreadLocalMap扩容机制和HashMap差不多,也是扩容为原来的2倍,然后进行扩容后在再散列,并设置新的阈值。
在这里插入图片描述

讲完了set()方法再来讲一讲get()方法。

get()方法首先获取到当前线程,然后调用getMap()方法得到当前线程对应的ThreadLocalMap。如果这个map不为null,就根据key得到对应Entry,并返回对应的value值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TnwBbfVj-1633258255979)(C:\Users\Jian\AppData\Roaming\Typora\typora-user-images\image-20211003180603028.png)]

如果map为null,比如在第一次调用get()方法,这个map可能还没创建出来。此时会调用setInitialValue()方法来设置初始值,

并返回这个value,value的默认初始值为null。
在这里插入图片描述

我们讲完了ThreadLocal类的set()get()方法,实际上都是一直在操作ThreadLocalMap这个map集合。而ThreadLocalMap中存放的都是一个个的Entry,Entry的key为ThreadLocal对象,value为对应的本地变量。实际上这个EntryWeakReference弱引用的子类,这是为了在JVM进行垃圾回收时,能够自动进行回收,防止内存溢出。真正存储数据备份其实就是这个Entry
在这里插入图片描述

ThreadLocal类本质实际上是以线程作为key,通过数据备份的方式,实现了线程间的数据隔离!

既然进行的数据备份,那么肯定就会造成数据冗余,并且随着线程的存活时间增长,存储的备份数据也会越来越多,即使线程结束了生命周期,这些备份数据也很有可能仍然存在。这样就可能造成内存泄漏,进而导致OOM!

ThreadLocal为了解决内存泄漏的问题,也进行了一些相应的处理,比如将存储备份数据的Entry类设置为弱引用类型,这是为了方便在GC时自动回收。而且在set()get()方法中增加了数据检查,及时清除掉那些key为null的没用的备份数据。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ThreadLocal从变量副本的角度解决多线程并发安全问题 的相关文章

随机推荐

  • react强制组件刷新_如何强制刷新React子组件:简单方法

    react强制组件刷新 Note As of React 16 componentWillReceiveProps is deprecated meaning if you are using that version or higher
  • JS组件Bootstrap实现弹出框和提示框效果代码

    这篇文章主要介绍了JS组件Bootstrap实现弹出框和提示框效果代码 对弹出框和提示框感兴趣的小伙伴们可以参考一下 前言 对于Web开发人员 弹出框和提示框的使用肯定不会陌生 比如常见的表格新增和编辑功能 一般常见的主要有两种处理方式 行
  • Python研发工程师必备工具合集

    Python研发工程师必备工具合集 1 必备工具 2 常用网站 3 学习路线 4 必备技能 5 书籍推荐 6 进阶学习 一 必备工具 1 Sublime Text 2 Notepad 3 Visual Studio Code 4 Atom
  • 宝塔环境,后台上传图片不成功问题

    主要是宝塔开启了post攻击拦截导致的 关闭之后就可以了 或者选择性关闭规则 我的项目追踪请求返回内容 div 宝塔免费WAF提醒您 from data 请求异常 拒绝访问 如有误报请点击误报 div
  • 第十章 数据库恢复技术

    第十章 数据库恢复技术 10 1 事务的基本概念 事务 事务是用户定义的一个数据库操作序列 这些操作要么全做 要么全不做 是一个不可分割的工作单位 例 事务的特性 ACID特性 ACID properties 原子性 Atomicity 事
  • Jetbrains相关IDE下载历史版本

    进入 https www jetbrains com zh cn phpstorm download other html 点击相应的IDE 在连接最后添加 字符串 download other html 回车 跳转到当前IDE历史版本下载
  • python3.6 安装torch、torchvision

    python3 6安装torch torchvision 1 进入torch的官网的下载页面 选择一下参数信息 2 下载 whl 文件到本地 直接复制那个地址 3 把刚刚下载的两个文件 放到安装python安装文件夹里面去 比如 我是复制到
  • C语言:详细讲解基于tcp和udp的两种本地通信方式

    udp本地通信注意 客户端这边必须要绑定自己的客户端信息不然服务器就无法给客户端发送消息 原因是网络编程的时候客户端信息操作系统会自动分配 而本地通信并不会 本地通信的时候 服务器的那个套接字文件是用来标识服务器进程的 客户端得用两个套接字
  • 集合的练习题

    练习1 随机点名器 需求 班级里有N个学生 实现随机点名器 public class W1随机点名器 public static void main String args ArrayList
  • OneNav 加强版支持多用户

    修改说明 支持多用户注册使用 Web自助申请 自助修改用户名和密码 可关闭注册 支持注册时记录用户注册IP和时间 支持登陆保护机制 多次登陆失败后会被限制 防止被爆破 支持隐藏登陆注册接口 防止被爆破 支持在非根目录运行 支持js css等
  • 以衍复为例,聊聊当下的沪深300指数增强

    最基础的问题 什么是沪深300指增 沪深300指数增强策略 对标沪深300指数 它利用量化投资的方式进行主动选股 很多管理人已经是全市场选股 在获取 指数 收益的同时 获取超越市场的 收益 能否长期获得稳定的 收益是衡量管理人水平的重要评判
  • [Pytorch系列-64]:生成对抗网络GAN - 图像生成开源项目pytorch-CycleGAN-and-pix2pix : 有监督图像生成pix2pix的基本原理

    作者主页 文火冰糖的硅基工坊 文火冰糖 王文兵 的博客 文火冰糖的硅基工坊 CSDN博客 本文网址 https blog csdn net HiWangWenBing article details 122044727 目录 第1章 关键参
  • Linux/Ubuntu 单机安装配置 zookeeper

    文章目录 下载 配置zookeeper 运行zookeeper 添加zookeeper命令到环境变量 下载 官网下载页面 https archive apache org dist zookeeper 清华源下载地址 https mirro
  • AI智能语音客服机器人方案介绍/优点/核心

    介绍 智能语音电话系统 在语音电话交流中自动理解客户意向 并做出最恰当的回应 智能代替人工的基本原理如下 即在呼入 呼出过程中 利用ASR NLP技术引导用户说出需求 通过真人录制的声音模仿与客户进行多轮对话 将语音转化为文字 根据识别的文
  • 查看数据库数据文件的总大小

    select round sum bytes 1024 1024 1024 0 total space GB from dba data files round 是oracle数据库四舍五入函数 来自 ITPUB博客 链接 http blo
  • 关于js逆向你必须知道的20个知识点

    1 什么是js逆向 js逆向是指通过分析JavaScript代码来了解网页或应用的工作原理 发现关键信息或漏洞的过程 2 为什么要学js逆向 学习js逆向可以帮助我们加深对JavaScript语言的理解 发现网站或应用的漏洞 也可以用于一些
  • 【H5】前端存储 -- cookie、sessionStorage、localStorage

    前端存储分为cookie和webStorage webStorage又分为sessionStorage 会话存储 和localStorage 本地存储 cookie 特点 cookie产生于服务器端 保存在客户端 同一服务器下的cookie
  • 知识图谱的应用领域

    1 3 知识图谱的价值 知识图谱最早的应用是提升搜索引擎的能力 随后 知识图谱在辅助智能问答 自然语言理解 大数据分析 推荐计算 物联网设备互联 可解释性人工智能等多个方面展现出丰富的应用价值 1 辅助搜索 互联网的终极形态是万物的互联 而
  • [VS Code]-代码高亮设置

    代码高亮设置 在vscode 中对 选中的代码片段高亮颜色设置 和 所在当前行高亮提示设置 workbench colorCustomizations 2 写配置代码 workbench colorCustomizations editor
  • ThreadLocal从变量副本的角度解决多线程并发安全问题

    ThreadLocal从变量副本的角度解决多线程并发安全问题 之前我们讲的高并发场景下的线程安全问题 可以使用Synchronized同步关键字 Lock手动加锁的方式去解决 什么轻量级锁 偏向锁 重量级锁 可重入锁等等 实际上本质都是控制