浅谈ThreadLocal

本文详细介绍了ThreadLocal的概念、用途、使用方法及其原理,通过实例展示了如何在多线程环境中利用ThreadLocal保持线程局部变量。同时,分析了ThreadLocal可能导致的内存泄漏问题,给出了避免内存泄漏的建议,帮助读者更好地理解和应用ThreadLocal。
摘要由CSDN通过智能技术生成

最近学习了秒杀系统,做一些必要的笔记,一来是对自己学习的知识的巩固,二来对有同样问题的人有参考作用



一 ThreadLocal是什么?有哪些用途?

  1. 总结
    所以有那么一句总结:ThreadLocal是线程Thread中属性threadLocals的管理者。也就是说我们对于ThreadLocal的get, set,remove的操作结果都是针对当前线程Thread实例的threadLocals存,取,删除操作。
  2. 举例
    在这里插入图片描述
    (1) 每个人都一张银行卡。
    (2)每个人每张卡都有一定的余额。
    (3)每个人获取银行卡余额都必须通过该银行的管理系统。
    (4)每个人都只能获取自己卡持有的余额信息,他人的不可访问。
    在这里插入图片描述
    映射到我们要说的ThreadLocal:
    (1)card类似于Thread。
    (2)card余额属性,卡号属性等类似于Treadlocal内部属性集合threadLocals。
    (3)cardManager类似于ThreadLocal管理类。
    总结:cardManager通过验证Card的信息取到Card某个属性的值
  3. 应用场景
       其实我们无意间已经时时刻刻在使用ThreadLocal提供的便利,如果说多数据源的切换你比较陌生,那么spring提供的声明式事务就再熟悉不过了,我们在研发过程中无时无刻不在使用,而spring声明式事务的重要实现基础就是ThreadLocal,只不过大家没有去深入研究spring声明式事务的实现机制。后面有机会我会给大家介绍spring声明式事务的原理及实现机制。
       原来ThreadLocal这么强大,但应用开发者使用较少,同时有些研发人员对于ThreadLocal内存泄漏,等潜在问题,不敢试用,恐怕这是对于ThreadLocal最大的误解,后面我们将会仔细分析,只要按照正确使用方式,就没什么问题。如果ThreadLocal存在问题,岂不是spring声明式事务是我们程序最大的潜在危险吗?

二 ThreadLocal如何使用

为了更直观的体会ThreadLocal的使用我们假设如下场景:
1.我们给每个线程生成一个ID。
2.一旦设置,线程生命周期内不可变化。
3.容器活动期间不可以生成重复的ID
我们创建一个ThreadLocal管理类:
在这里插入图片描述
   测试程序如下:我们同一个线程不断get,测试id是否变化,同时测试完成后我们就将其释放掉。
在这里插入图片描述
   在主程序中我们开启多个线程测试不通线程之间是否会影响:
在这里插入图片描述
不出意外我们的结果为:
在这里插入图片描述
   结果:确实是不同线程间ThreadLocal的id不同,相同线程ThreadLocal的id相同。
   结论:ThreadLocal与线程是一一对应的,且是线程安全的。

三 ThreadLocal原理

  1. ThreadLocal类结构及方法解析:
    在这里插入图片描述
    上图可知:ThreadLocal三个方法get, set , remove以及内部类`ThreadLocalMap。
  2. ThreadLocal及Thread之间的关系:
    在这里插入图片描述
       从这张图我们可以直观的看到Thread中属性threadLocals,作为一个特殊的Map,它的key值就是我们ThreadLocal实例,而value值这是我们设置的值。
  3. ThreadLocal的操作过程:
    我们以get方法为例:
    在这里插入图片描述
       其中getMap(t)返回的就上当前线程的threadlocals,如下图,然后根据当前ThreadLocal实例对象作为key获取ThreadLocalMap中的value,如果首次进来这调用setInitialValue()
    在这里插入图片描述
    在这里插入图片描述
    set的过程也类似:
    在这里插入图片描述

四 ThreadLocal使用有哪些坑及注意事项

   经常在网上看到骇人听闻的标题,ThreadLocal导致内存泄漏,这通常让一些刚开始对ThreadLocal理解不透彻的开发者,不敢贸然使用。越不用,越陌生。这样就让我们错失了更好的实现方案,所以敢于引入新技术,敢于踩坑,才能不断进步。
我们来看下为什么说ThreadLocal会引起内存泄漏,什么场景下会导致内存泄漏?

Memory overflow:内存溢出,没有足够的内存提供申请者使用。
Memory leak:内存泄漏,程序申请内存后,无法释放已申请的内存空间,内存泄漏的堆积终将导致内存溢出。
显然是TreadLocal在不规范使用的情况下导致了内存没有释放。

在这里插入图片描述
   红框里我们看到了一个特殊的类WeakReference,同样这个类,应用开发者也同样很少使用,这里简单介绍下吧
在这里插入图片描述
   既然WeakReference在下一次gc即将被回收,那么我们的程序为什么没有出问题呢?
   1. 所以我们测试下弱引用的回收机制:
在这里插入图片描述
   这一种存在强引用不会被回收。
在这里插入图片描述
   这里没有强引用将会被回收。
   上面演示了弱引用的回收情况,下面我们看下ThreadLocal的弱引用回收情况。
   2. ThreadLocal的弱引用回收情况
在这里插入图片描述
   如上图所示,我们在作为key的ThreadLocal对象没有外部强引用,下一次gc必将产生key值为null的数据,若线程没有及时结束必然出现,一条强引用链
   Threadref–>Thread–>ThreadLocalMap–>Entry,所以这将导致内存泄漏。
3. 所以我们总结了使用ThreadLocal时会发生内存泄漏的前提条件:

a.ThreadLocal引用被设置为null,且后面没有set,get,remove操作。
b.线程一直运行,不停止。(线程池)
c.触发了垃圾回收。(Minor GC或Full GC)

  解决方法:
         我们看到ThreadLocal出现内存泄漏条件还是很苛刻的,所以我们只要破坏其中一个条件就可以避免内存泄漏,单但为了更好的避免这种情况的发生我们使用ThreadLocal时遵守以下两个小原则:

a.ThreadLocal申明为private static final。
    Private与final 尽可能不让他人修改变更引用,
    Static 表示为类属性,只有在程序结束才会被回收。
b.ThreadLocal使用后务必调用remove方法。
    最简单有效的方法是使用后将其移除。

五 总结

   如有错误恳请指正,如有侵权请联系我删除
   参考文章:https://blog.csdn.net/zzg1229059735/article/details/82715741

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
修改ThreadLocal的值可以通过以下步骤实现: 1. 首先,创建一个ThreadLocal对象,并将其初始化为需要修改的值。可以使用ThreadLocal的`set()`方法来设置值。例如,`threadLocal.set(value)`,其中value是要设置的新值。 2. 接下来,可以在需要修改ThreadLocal值的线程中使用上述设置的ThreadLocal对象。可以通过`threadLocal.get()`方法来获取该线程的ThreadLocal值。 3. 然后,对获取的ThreadLocal值进行修改,可以使用任何适当的方法来更改值。 4. 最后,使用ThreadLocal的`set()`方法将修改后的值设置回ThreadLocal对象中。例如,`threadLocal.set(modifiedValue)`,其中modifiedValue是修改后的值。 需要注意的是,每个线程都有自己独立的ThreadLocal副本,因此在不同的线程中修改ThreadLocal值不会相互影响。这是ThreadLocal的特性之一,使得每个线程可以独立地管理自己的数据。 总结起来,修改ThreadLocal的值需要创建ThreadLocal对象、设置新值、获取当前线程的ThreadLocal值、修改该值,最后将修改后的值设置回ThreadLocal对象中。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [浅谈ThreadLocal](https://blog.csdn.net/qq_36609994/article/details/121163994)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [ThreadLocal](https://blog.csdn.net/weixin_43573186/article/details/125068926)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值