Java并发之ThreadLocal

Java并发 ThreadLocal 原理(详解) 比如 Tomcat 的线程池处理了一堆请求,而线程池中的线程一般是不会被清理掉的,所以这个引用链就会一直在,那么 ThreadLocal 对象即使没有用了,也会随着线程的存在,而一直存在着。对象作为键都会 计算得到一个hash值,然后和hashMap一样,计算得到entry数组内的一个下标,去查找这个threadLocal对象。实例被不再需要的线程持有为强引用,那么当该线程结束时,相关的 ThreadLocal实例。当然,这种数组操作一般免不了阈值的判断,如果超过阈值则需要进行扩容。 阅读详情

前言

ThreadLocal是一个全局变量,用于解决线程范围内变量共享问题,其实ThreadLocal可以看作是一个Map集合,而key就是当前线程名,而value是想要存放的变量,ThreadLocal的变量用完会自动销毁,不用考虑ThreadLocal中的变量会占用空间,对于ThreadLocal来说每个线程的变量都是独立的,不会相互影响,可以通过ThreadLocal在同一线程内的不同组件传递公共变量

成员变量

我们看ThreadLocal的源码可以看到他有三个成员变量

<pre class="prettyprint hljs gradle" deep="5" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">/**
    这个属性用于ThreadLocal的桶位寻址
    如果一个threadLocal对象第一次调用get()方法,那么就会给当前线程分配一个value
    这个value和这个threadLocal对象被包装成一个entry
    这个entry的key是threadLocal对象,value是生成的value
    **/
    private final int threadLocalHashCode = nextHashCode();
    /**
    这个属性是创建ThreadLocal会用到该属性
    每创建一个ThreadLocal对象,就会使用该属性分配一个hash值给这个对象
    **/
    private static AtomicInteger nextHashCode = new AtomicInteger();
    /**
    表示hash增量,没创建一个ThreadLocal对象,ThreadLocal.nextHashCode就会增长HASH_INCREMENT
    **/
    private static final int HASH_INCREMENT = 0x61c88647;

get方法

<pre class="prettyprint hljs kotlin" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public T get() {
    //获取当前线程
    Thread t = Thread.currentThread();
    //根据当前线程获取ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    //判断map是否为null
    if (map != null) {
        //当前threadLocal对象作为key获取entry
        ThreadLocalMap.Entry e = map.getEntry(this);
        //判断entry是否为null
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            //返回result
            return result;
        }
    }
    //初始化
    return setInitialValue();
}

接下来我们再看这个初始化方法

setInitialValue方法

这个方法是用于初始化的,我们从上面的get方法可以知道,进入该方法有两种情况,一个就是ThreadLocalMap为null或者Entry为null

<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">private T setInitialValue() {
        //调用ThreadLocal对象的方法进行初始化
        T value = initialValue();
        //获取到当前线程
        Thread t = Thread.currentThread();
        //当前线程作为key获取ThreadLocalMap对象
        ThreadLocalMap map = getMap(t);
        //判断map是否为null,如果不为null证明已经初始化过ThreadLocalMap对象了(一个ThreadLocal只会初始化一次)
        if (map != null)
            //向ThreadLocalMap中保存当前threadLocal与当前线程生成的线程局部变量
            map.set(this, value);
        else
            //如果走到这里证明map==null,进行初始化创建一个ThreadLocalMap对象
            createMap(t, value);
        return value;
    }

set方法

<pre class="prettyprint hljs cs" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, &quot;Courier New&quot;, monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; overflow-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public void set(T value) {
    //获取当前线程
    Thread t = Thread.currentThread();
    //将当前线程作为key获取ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    //判断threadLocalMap是否为null
    if (map != null)
        //覆盖或者添加新的key - value
        map.set(this, value);
    else
        //证明还没有被初始化,进行初始化
        createMap(t, value);
}

如何实现线程隔离?

每一个Thread都有着自己的ThreadLocalMap用于存储数据,当线程访问某个ThreadLocal对象的get方法的时候,方法内部会判断该线程的ThreadLocalMap数组内是否存在以当前线程为key的Entry节点,如果数组内没有对应的节点,那么当前ThreadLocal对象,就会调用初始化方法,创建一个Entry节点放入ThreadLocalMap中

ThreadLocal内存泄漏原因

主要是因为ThreadLocalMap的Entry对象是弱引用,对于弱引用,JVM在每次GC的时候都会清理他们,此时key就会变为null,如果此时没有其他强引用指向value,那么此时value就不会被访问到,按道理来说value应该也会被GC,但是此时ThreadLocalMap的Entry对象对value是强引用,这样就会导致JVM始终无法GC value,这样就导致了value成了一个始终无法清理的value

java使用(ThreadLocal、ReentrantLock(JVM锁)、Redisson分布式锁)解决全局共享变量并发安全问题 (当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本)3、此问题可以使用自定义局部变量中转处理解决,也可以使用 ThreadLocal、JVM锁(ReentrantLock)、分布式锁(Redisson) 来解决。2、因为有可能本次线程赋值后还没处理完业务,后续的线程已经把变量值改掉,那么这时候本次线程拿到的就是其他线程赋的值,这个时候会出现并发问题,导致变量值混乱。 阅读详情

相关推荐

【Java并发编程】ThreadLocal由浅入深说

不要停留在用,一文带你了解ThreadLocal底层

sowhat 3636

ThreadLocal 辨析

ThreadLocal 为每个线程都提供了变量的副本, 使得每个线程在某一时间訪问到的并非同一个对象, 这样就隔离了多个线 程对数据的数据共享。Spring 的事务就借助了 ThreadLocal 类。 Spring 会从数据库连接池中获得一个connection, 然会connection 放进 ThreadLocal 中, 也就和线程绑定了, 事务需要提交或者回滚, 只要从 ThreadLo...

krisyan的博客 196

万象RK3506-EG1800网关使用说明

摘要:HD-RK3506-EG1800是一款高性能工业物联网边缘计算网关,支持Buildroot和Ubuntu双系统。文档详细介绍了网关的网络配置(包括ETH0/ETH1的静态IP设置、WiFi连接)、时钟同步、HSL软件安装配置(含开机自启动服务)、系统日志优化以及刷机操作指南(Loader/Maskrom模式)。特别提供了基于lighttpd的网页管理方案,通过HTML界面实现网络参数可视化配置,包含身份验证、实时状态显示和表单提交功能。网关默认ETH0固定IP为192.168.1.10,提供完整的CL

tian_jiangnan的博客 174

关于ThreadLocal的详细解析

ThreadLocal 是什么?ThreadLocal是一个为每个线程创建单独变量副本的类。ThreadLocal主要解决的是让每一个线程绑定自己的值,自己用自己的,不跟别的线程争抢。该类提供了线程局部 (thread-local) 变量。通过使用get()和set()方法可以访问线程自己的、独立于初始化的变量副本。即通过ThreadLocal可以让指定的值实现线程隔离(线程之间不存在共享关系),从而避免了线程安全的问题。ThreadLocal 的常用API。

2301_76607156的博客 1737

FoundationPose实战:从零部署到Demo运行全记录

本文详细记录了FoundationPose从零部署到Demo运行的全过程,包括Ubuntu环境配置、项目部署、依赖安装及常见问题解决方案。特别针对复现过程中的关键步骤如CUDA版本选择、Eigen3安装等提供了实用技巧,帮助开发者高效完成FoundationPose的部署与调试。

weixin_27102543的博客 267

高速接口电平标准互连设计:LVPECL、PECL与LVDS的实战解析

本文深入解析了LVPECL、PECL与LVDS三种高速接口电平标准的互连设计实战。针对信号共模电压、阻抗匹配等核心差异,详细对比了直流耦合与交流耦合两种主流方案,并提供了电阻网络计算、布局布线要点及光模块等特殊场景下的选型建议,帮助工程师解决高速信号间的“翻译”难题,确保信号完整性。

weixin_29198045的博客 534

Java使用ThreadLocal保存全局变量

【代码】Java使用ThreadLocal保存全局变量。

weng74的博客 684

手把手教你用CH582的USB0/1双接口玩转CherryUSB协议栈(附完整代码)

本文深入解析CH582双USB接口硬件架构,详细讲解如何通过CherryUSB协议栈实现双接口协同工作,包括中断处理设计、端点资源分配和复合设备描述符配置。提供HID+CDC复合设备实战案例,并分享高级调试技巧与性能优化方法,帮助开发者充分利用CH582的USB0/1双接口潜力。

weixin_42525637的博客 264

Java并发——ThreadLocal总结

并发问题,有时候,可以用ThreadLocal方式来避免。ThreadLocal,顾名思义,就是线程自己的,独享的,就像线程栈是线程独享的一样。

BASK2311的博客 984

告别虚拟机!在WSL2的Ubuntu里5分钟跑通LVGL v9.2图形界面(附SDL2配置避坑)

本文详细介绍了如何在WSL2的Ubuntu子系统中快速配置LVGL v9.2图形库的SDL2模拟环境,帮助嵌入式开发者在5分钟内跑通第一个图形界面示例。通过WSL2和VSCode的远程开发插件,实现Windows系统下无缝使用Linux工具链,大幅提升开发效率。

weixin_28736335的博客 422

用Python和NumPy搞定复合材料层合板ABD矩阵计算(附完整代码与避坑指南)

本文详细介绍了如何使用Python和NumPy高效计算复合材料层合板的ABD刚度矩阵,包括单层板刚度计算、偏轴转换和层合板刚度集成。通过完整的代码示例和工程实践技巧,帮助工程师快速构建定制化分析工具,解决航空航天、汽车制造等领域的复合材料力学性能分析问题。特别提供了避坑指南和性能优化策略,确保计算结果的准确性和效率。

weixin_30752699的博客 277

java并发之ThreadLocal

ThreadLocal的思考: 描述: 方法1:传入秒数,new一个SimpleDataFormat对象,返回时间字符串 方法2:生成两个线程,每个线程都去调用方法1来打印不同秒数的时间 结果:运行正常 进一步思考:要是1000个线程呢?用线程池,生成10线程 进一步思考:在方法1中,每次调用都会new一个SimpleDataFormat对象,浪费开销 解决方法:将S...

简单点,编程的方式简单点 270

MATLAB算法实战应用案例精讲-【数模应用】装箱问题

装箱问题可以根据不同的维度进行分类。以下是一些常见的装箱问题二维装箱问题:二维装箱问题假设所有物品都是平面上的矩形,并且需要将它们放入平面上的容器中。这个问题的目标是找到一种装箱方案,使得物品不重叠并占据的总面积最小。三维装箱问题:三维装箱问题考虑到物品可以具有高度,并且需要将它们放入三维容器(如箱子或货柜)中。目标是找到一种装箱方案,使得物品不重叠且总体积最小。多维装箱问题:多维装箱问题是指在更高维度空间中进行装箱的问题。除了长度和宽度之外,物品可能具有其他属性,如高度、重量、形状等。

qq_36130719的博客 1571

【Java 并发】详解 ThreadLocal

前言 ThreadLocal 主要用来提供线程局部变量,也就是变量只对当前线程可见,本文主要记录一下对于 ThreadLocal 的理解。更多关于 Java 多线程的文章可以转到 这里。 线程局部变量 在多线程环境下,之所以会有并发问题,就是因为不同的线程会同时访问同一个共享变量,例如下面的形式 public class MultiThreadDemo { public static class Number { private int value = 0; pu

weixin_53341657的博客 257

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Java 并发 - ThreadLocal原理

Java 并发 - ThreadLocal原理 线性探测

杨宇昌的博客 398

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

java并发编程之ThreadLocal

不管用那种语言,在并发编程中遇见的问题都是类似的,只不每种语言提供的方法和工具不一样而已。 对于每个线程独享某个对象或者每个线程独立保存信息的场景,java提供的解决方法是ThreadLocal , 对于线程独享某个对象的例子 public class ThreadLocalDemo { public static ExecutorService threadPool = Executors.newFixedThreadPool(16); SimpleDateFormat dateForma

acethan的博客 186

Java并发编程之 ThreadLocal

多线程访问同一个共享变量的时候容易出现并发问题,特别是多个线程对一个变量进行写入的时候,为了保证线程安全,一般使用者在访问共享变量的时候需要进行额外的同步措施才能保证线程安全性。ThreadLocal是除了加锁这种同步方式之外的另一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。 ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线

qq_43613793的博客 582

Java并发编程——ThreadLocal

ThreadLocal详解

m0_62909831的博客 1242

ThreadLocal实战:Java并发编程中的利器

要点 | 描述 || ThreadLocal | 用于线程隔离的利器,适用于高并发场景 || 线程安全 | 通过为每个线程绑定变量副本实现线程之间的数据隔离 || 性能 | 高吞吐量、低延迟 |ThreadLocal是Java并发编程中的利器,在高并发场景下,可以有效解决线程安全问题。随着技术的不断发展,ThreadLocal将在更多场景中得到应用。未来,我们期待看到更多关于ThreadLocal的创新和突破。

南客先生的博客 870

Java并发-ThreadLocal

ThreadLocal的作用 作用是为每个线程保存线程私有数据。可以把ThreadLocal理解为一个Map,其中key为当前线程,value为要存储的数据。 下面是摘自《Java编程思想》中的一段示例代码: public class ThreadLocalTest { public static void main(String[] args) throws Exceptio...

不瘦十斤不换名字 576

java threadlocal 并发_java 并发,为线程创建本地变量 ThreadLocal的使用

packagetest;publicclassThreadLocalTest{publicstaticvoidmain(String[]args){ThreadLocaltl=newThreadLocal();Locall=newLocal();ThreadAta=newThreadA(tl);ThreadAtb=newThreadA(tl);ta...

weixin_39787089的博客 95

[Java][并发]ThreadLocal分析

nanchengyu的博客 150

【Java并发】ThreadLocal

ThreadLocal是java.lang下面的一个类,是用来解决java多线程程序中并发问题的一种途径;通过为每一个线程创建一份共享变量的副本来保证各个线程之间的变量的访问和修改互相不影响;ThreadLocal存放的值是线程内共享的,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递,这样处理后,能够优雅的解决一些实际问题。

xueping_wu的博客 785
上一篇: Java 六边形架构 – BABAL
下一篇: Java中泛型——类型擦除
Java_ttcd
博客等级 码龄4年 198粉丝 190原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值