ThreadLocal底层原理及数据结构详解

devtools/2024/10/11 3:21:26/

  ThreadLocal允许为每个线程创建独立的变量副本,使得同一个ThreadLocal对象在不同的线程中拥有不同的值。它的主要作用是在并发环境下提供线程隔离,避免多个线程共享同一个变量,从而减少线程间的相互干扰。

  ThreadLocal的核心在于为每个线程维护一个独立的数据副本,它的实现主要依赖于每个线程维护一个ThreadLocalMap,这是ThreadLocal专用的Map,用来存储线程自己的变量。

1.1. ThreadLocalMap底层数据结构

  ThreadLocalMap是一个定制化的Map,其结构类似于HashMap,都是以Key-Value的键值对形式进行存储,其中Key存储的是ThreadLocal实例Value存储的是对应的对象,默认为Object。相比于HashMap有一些不同之处:

  • 弱引用的键:ThreadLocalMap的键(即ThreadLocal对象)使用了弱引用(强引用>软引用>弱应用>虚引用),因此当没有其他地方引用该ThreadLocal对象时,GC就会回收它。
  • 线性探测解决哈希冲突:区别于HashMap中的链地址法解决哈希冲突,ThreadLocalMap使用线性探测法来解决哈希冲突,并且负载因子为2/3。
  • 潜在内存泄漏:由于ThreadLocalMap中的键是弱引用,但其存储的Value是强应用,如果ThreadLocal对象被GC回收,而没有调用remove()方法清理值,那么ThreadLocalMap中的值就有可能会一直存在,导致内存泄漏。因此在不适用ThreadLocal后,要及时的调用remove()方法,手动清除线程的副本变量。或者使用 try-finally 模式来保证在完成工作后调用 remove()
1.2. 能否使用ThreadLocal往线程中存储多个副本变量?

         默认情况下,ThreadLocal每个线程只能存储一个值,因为它的设计初衷就是让每个线程独立的维护一组与ThreadLocal对象相关的值,也就是说,每个ThreadLocal实例只能存储一个值。

        虽然 ThreadLocal 本身每个实例只能存储一个值,但多个 ThreadLocal 实例在同一个线程中是存储在 ThreadLocalMap 里的。因此,当一个线程中存在多个 ThreadLocal 实例时,这些实例及其对应的值就会存储在该线程的 ThreadLocalMap 中。

        那如果我们就是想让一个线程拥有多个副本变量该怎么办?

  • 法一:使用ThreadLocal存储一个容器(如Map或自定义对象)

        虽然每个ThreadLocal实例只能存储一个值,但是其存储的是什么值是由我们决定的,因此可以将想要存储的多个变量放入Map中,以此实现存储多个独立的副本变量。

  • 法二:使用多个ThreadLocal对象
private static ThreadLocal<String> threadLocal1 = new ThreadLocal<>();
private static ThreadLocal<Integer> threadLocal2 = new ThreadLocal<>();
threadLocal1.set("Thread1");
threadLocal2.set("Thread2");

        通过以上代码在每个线程的ThreadLocalMap中创建了两个ThreadLocal对象,分别存储"Thread1"和"Thread2",因此可以通过不同的ThreadLocal实例对象来获取不同的值。


http://www.ppmy.cn/devtools/122034.html

相关文章

如何伪装服务器地址?

在现代网络环境中&#xff0c;出于隐私、安全或绕过限制的目的&#xff0c;伪装服务器地址成为一种重要的技术手段。伪装服务器地址的目的主要是隐藏服务器的真实IP&#xff0c;以防止恶意攻击、数据追踪或规避某些网络限制。在没有提到VPN的前提下&#xff0c;仍然有多种方法可…

flutter_鸿蒙next_Dart基础①字符串

目录 代码示例 代码逐段解析 1. 字符串的声明与打印 2. 数字的声明与打印 3. 多行字符串 4. 字符串拼接 5. 字符串分割 6. 字符串修剪 7. 检查字符串是否为空 8. 字符串替换 9. 正则表达式与电话号码验证 10. 字符串查找 11. 字符串定位 写在最后 在本篇博客中&a…

Webstorm 中对 Node.js 后端项目进行断点调试

首先&#xff0c;肯定需要有一个启动服务器的命令脚本。 然后&#xff0c;写一个 debug 的配置&#xff1a; 然后&#xff0c;debug 模式 启动项目和 启动调试服务&#xff1a; 最后&#xff0c;发送请求&#xff0c;即可调试&#xff1a; 这几个关键按钮含义&#xff1a; 重启…

【MySQL】Ubuntu环境下MySQL的安装与卸载

目录 1.MYSQL的安装 2.MySQL的登录 3.MYSQL的卸载 4.设置配置文件 1.MYSQL的安装 首先我们要看看我们环境里面有没有已经安装好的MySQL 我们发现是默认是没有的。 我们还可以通过下面这个命令来确认有没有mysql的安装包 首先我们得知道我们当前的系统版本是什么 lsb_…

iOS--RunLoop原理

前言 曾经在写项目的时候遇到过这么一个问题。&#xff1a; 项目中添加了一个tableview&#xff0c;然后还有一个计时器&#xff0c;当滑动tableview的时候会阻塞计时器&#xff0c;你得执行这么一段代码后&#xff0c;计时器才能正常运行。 RunLoop.current.add(timer, for…

核心理论框架对比:如何通过TOGAF实现企业数字化转型的全面战略

数字化转型中的理论与实践融合 随着全球市场竞争的加剧和技术的快速革新&#xff0c;企业必须不断适应数字化技术带来的变化。数字化转型不仅是技术的升级&#xff0c;更是企业业务模式、组织架构以及文化的深度变革。然而&#xff0c;在面对复杂的技术和业务环境时&#xff0…

《深度学习》OpenCV 摄像头OCR 过程及案例解析

目录 一、摄像头OCR 1、含义 2、一般操作步骤 1&#xff09;安装OpenCV库 2&#xff09;设置摄像头 3&#xff09;图像采集 4&#xff09;图像预处理 5&#xff09;文本识别 6&#xff09;文本处理 7&#xff09;结果显示 二、案例实现 1、定义展示图像函数 2、定…

OpenJudge | 置换选择排序

总时间限制: 1000ms 内存限制: 65536kB 描述 给定初始整数顺串&#xff0c;以及大小固定并且初始元素已知的二叉最小堆&#xff08;为完全二叉树或类似完全二叉树&#xff0c;且父元素键值总小于等于任何一个子结点的键值&#xff09;&#xff0c;要求利用堆实现置换选择排序&a…