全方位,多角度理解ThreadLocal(强烈推荐)

沙海 2021年8月30日12:08:05Java评论22字数 5770阅读19分14秒阅读模式
摘要

智能摘要

智能摘要文章源自JAVA秀-https://www.javaxiu.com/43235.html

我们发现Thread并没有提供成员变量threadLocals的设置与访问的方法,那么每个线程的实例threadLocals参数我们如何操作呢?其实我们无意间已经时时刻刻在使用ThreadLocal提供的便利,如果说多数据源的切换你比较陌生,那么spring提供的声明式事务就再熟悉不过了,我们在研发过程中无时无刻不在使用,而spring声明式事务的重要实现基础就是ThreadLocal,只不过大家没有去深入研究spring声明式事务的实现机制。文章源自JAVA秀-https://www.javaxiu.com/43235.html

原文约 5913 | 图片 28 | 建议阅读 12 分钟 | 评价反馈文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)

点击关注 ? Java基基 文章源自JAVA秀-https://www.javaxiu.com/43235.html

收录于话题#Java基基,345个文章源自JAVA秀-https://www.javaxiu.com/43235.html

点击上方“Java基基”,选择“设为星标”文章源自JAVA秀-https://www.javaxiu.com/43235.html

做积极的人,而不是积极废人!文章源自JAVA秀-https://www.javaxiu.com/43235.html

每天 14:00 更新文章,每天掉亿点点头发...文章源自JAVA秀-https://www.javaxiu.com/43235.html

文章源自JAVA秀-https://www.javaxiu.com/43235.html

源码精品专栏文章源自JAVA秀-https://www.javaxiu.com/43235.html

 文章源自JAVA秀-https://www.javaxiu.com/43235.html

文章源自JAVA秀-https://www.javaxiu.com/43235.html

来源:blog.csdn.net/zzg1229059735/文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

本次给大家介绍重要的工具ThreadLocal。讲解内容如下,同时介绍什么场景下发生内存泄漏,如何复现内存泄漏,如何正确使用它来避免内存泄漏。文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ThreadLocal是什么?有哪些用途?文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ThreadLocal如何使用文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ThreadLocal原理文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ThreadLocal使用有哪些坑及注意事项文章源自JAVA秀-https://www.javaxiu.com/43235.html

1.ThreadLocal是什么?有哪些用途?

首先介绍Thread类中属性threadLocals:文章源自JAVA秀-https://www.javaxiu.com/43235.html

/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ThreadLocal.ThreadLocalMap threadLocals = null;

我们发现Thread并没有提供成员变量threadLocals的设置与访问的方法,那么每个线程的实例threadLocals参数我们如何操作呢?这时我们的主角:ThreadLocal就登场了。文章源自JAVA秀-https://www.javaxiu.com/43235.html

所以有那么一句总结:ThreadLocal是线程Thread中属性threadLocals的管理者。文章源自JAVA秀-https://www.javaxiu.com/43235.html

也就是说我们对于ThreadLocal的get, set,remove的操作结果都是针对当前线程Thread实例的threadLocals存,取,删除操作。类似于一个开发者的任务,产品经理左右不了,产品经理只能通过技术leader来给开发者分配任务。下面再举个栗子,进一步说明他们之间的关系:文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

  1. 每个人都一张银行卡文章源自JAVA秀-https://www.javaxiu.com/43235.html

  2. 每个人每张卡都有一定的余额。文章源自JAVA秀-https://www.javaxiu.com/43235.html

  3. 每个人获取银行卡余额都必须通过该银行的管理系统。文章源自JAVA秀-https://www.javaxiu.com/43235.html

  4. 每个人都只能获取自己卡持有的余额信息,他人的不可访问。文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

映射到我们要说的ThreadLocal文章源自JAVA秀-https://www.javaxiu.com/43235.html

  1. card类似于Thread文章源自JAVA秀-https://www.javaxiu.com/43235.html

  2. card余额属性,卡号属性等类似于Treadlocal内部属性集合threadLocals文章源自JAVA秀-https://www.javaxiu.com/43235.html

  3. cardManager类似于ThreadLocal管理类文章源自JAVA秀-https://www.javaxiu.com/43235.html

那ThreadLocal有哪些应用场景呢?文章源自JAVA秀-https://www.javaxiu.com/43235.html

其实我们无意间已经时时刻刻在使用ThreadLocal提供的便利,如果说多数据源的切换你比较陌生,那么spring提供的声明式事务就再熟悉不过了,我们在研发过程中无时无刻不在使用,而spring声明式事务的重要实现基础就是ThreadLocal,只不过大家没有去深入研究spring声明式事务的实现机制。后面有机会我会给大家介绍spring声明式事务的原理及实现机制。文章源自JAVA秀-https://www.javaxiu.com/43235.html

原来ThreadLocal这么强大,但应用开发者使用较少,同时有些研发人员对于ThreadLocal内存泄漏,等潜在问题,不敢试用,恐怕这是对于ThreadLocal最大的误解,后面我们将会仔细分析,只要按照正确使用方式,就没什么问题。如果ThreadLocal存在问题,岂不是spring声明式事务是我们程序最大的潜在危险吗?文章源自JAVA秀-https://www.javaxiu.com/43235.html

推荐下自己做的 Spring Boot 的实战项目:文章源自JAVA秀-https://www.javaxiu.com/43235.html

https://github.com/YunaiV/ruoyi-vue-pro文章源自JAVA秀-https://www.javaxiu.com/43235.html

2.ThreadLocal如何使用

为了更直观的体会ThreadLocal的使用我们假设如下场景文章源自JAVA秀-https://www.javaxiu.com/43235.html

  1. 我们给每个线程生成一个ID。文章源自JAVA秀-https://www.javaxiu.com/43235.html

  2. 一旦设置,线程生命周期内不可变化。文章源自JAVA秀-https://www.javaxiu.com/43235.html

  3. 容器活动期间不可以生成重复的ID文章源自JAVA秀-https://www.javaxiu.com/43235.html

我们创建一个ThreadLocal管理类:文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

测试程序如下:我们同一个线程不断get,测试id是否变化,同时测试完成后我们就将其释放掉。文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

在主程序中我们开启多个线程测试不通线程之间是否会影响文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

不出意外我们的结果为:文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

结果:确实是不同线程间id不同,相同线程id相同。文章源自JAVA秀-https://www.javaxiu.com/43235.html

推荐下自己做的 Spring Cloud 的实战项目:文章源自JAVA秀-https://www.javaxiu.com/43235.html

https://github.com/YunaiV/onemall文章源自JAVA秀-https://www.javaxiu.com/43235.html

3.ThreadLocal原理

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

上图可知:ThreadLocal三个方法get, set , remove以及内部类ThreadLocalMap文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

从这张图我们可以直观的看到Thread中属性threadLocals,作为一个特殊的Map,它的key值就是我们ThreadLocal实例,而value值这是我们设置的值。文章源自JAVA秀-https://www.javaxiu.com/43235.html

我们以get方法为例:文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

其中getMap(t)返回的就上当前线程的threadlocals,如下图,然后根据当前ThreadLocal实例对象作为key获取ThreadLocalMap中的value,如果首次进来这调用setInitialValue()文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

set的过程也类似:文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

注意:ThreadLocal中可以直接t.threadLocals是因为Thread与ThreadLocal在同一个包下,同样Thread可以直接访问ThreadLocal.ThreadLocalMap threadLocals = null;来进行声明属性。文章源自JAVA秀-https://www.javaxiu.com/43235.html

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

我经常在网上看到骇人听闻的标题,ThreadLocal导致内存泄漏,这通常让一些刚开始对ThreadLocal理解不透彻的开发者,不敢贸然使用。越不用,越陌生。这样就让我们错失了更好的实现方案,所以敢于引入新技术,敢于踩坑,才能不断进步。文章源自JAVA秀-https://www.javaxiu.com/43235.html

我们来看下为什么说ThreadLocal会引起内存泄漏,什么场景下会导致内存泄漏?文章源自JAVA秀-https://www.javaxiu.com/43235.html

先回顾下什么叫内存泄漏,对应的什么叫内存溢出文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ①Memory overflow:内存溢出,没有足够的内存提供申请者使用。文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ②Memory leak:内存泄漏,程序申请内存后,无法释放已申请的内存空间,内存泄漏的堆积终将导致内存溢出。文章源自JAVA秀-https://www.javaxiu.com/43235.html

显然是TreadLocal在不规范使用的情况下导致了内存没有释放。文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

红框里我们看到了一个特殊的类WeakReference,同样这个类,应用开发者也同样很少使用,这里简单介绍下吧文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

既然WeakReference在下一次gc即将被回收,那么我们的程序为什么没有出问题呢?文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

这一种存在强引用不会被回收。文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

这里没有强引用将会被回收。文章源自JAVA秀-https://www.javaxiu.com/43235.html

上面演示了弱引用的回收情况,下面我们看下ThreadLocal的弱引用回收情况。文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

如上图所示,我们在作为key的ThreadLocal对象没有外部强引用,下一次gc必将产生key值为null的数据,若线程没有及时结束必然出现,一条强引用链Threadref–>Thread–>ThreadLocalMap–>Entry,所以这将导致内存泄漏。文章源自JAVA秀-https://www.javaxiu.com/43235.html

下面我们模拟复现ThreadLocal导致内存泄漏:文章源自JAVA秀-https://www.javaxiu.com/43235.html

1.为了效果更佳明显我们将我们的treadlocals的存储值value设置为1万字符串的列表:文章源自JAVA秀-https://www.javaxiu.com/43235.html

class ThreadLocalMemory {    // Thread local variable containing each thread's ID    public ThreadLocal<List<Object>> threadId = new ThreadLocal<List<Object>>() {        @Override        protected List<Object> initialValue() {            List<Object> list = new ArrayList<Object>();            for (int i = 0; i < 10000; i++) {                list.add(String.valueOf(i));            }            return list;        }    };    // Returns the current thread's unique ID, assigning it if necessary    public List<Object> get() {        return threadId.get();    }    // remove currentid    public void remove() {        threadId.remove();    }}

测试代码如下:文章源自JAVA秀-https://www.javaxiu.com/43235.html

    public static void main(String[] args)            throws InterruptedException {        //  为了复现key被回收的场景,我们使用临时变量        ThreadLocalMemory memeory = new ThreadLocalMemory();        // 调用        incrementSameThreadId(memeory);        System.out.println("GC前:key:" + memeory.threadId);        System.out.println("GC前:value-size:" + refelectThreadLocals(Thread.currentThread()));        // 设置为null,调用gc并不一定触发垃圾回收,但是可以通过java提供的一些工具进行手工触发gc回收。        memeory.threadId = null;        System.gc();        System.out.println("GC后:key:" + memeory.threadId);        System.out.println("GC后:value-size:" + refelectThreadLocals(Thread.currentThread()));        // 模拟线程一直运行        while (true) {        }    }

此时我们如何知道内存中存在memory leak呢?文章源自JAVA秀-https://www.javaxiu.com/43235.html

我们可以借助jdk提供的一些命令dump当前堆内存,命令如下:文章源自JAVA秀-https://www.javaxiu.com/43235.html

jmap -dump:live,format=b,file=heap.bin <pid>

然后我们借助MAT可视化分析工具,来查看对内存,分析对象实例的存活状态:文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

首先打开我们工具提示我们的内存泄漏分析:文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

这里我们可以确定的是ThreadLocalMap实例的Entry.value是没有被回收的。文章源自JAVA秀-https://www.javaxiu.com/43235.html

最后我们要确定Entry.key是否还在?打开Dominator Tree,搜索我们的ThreadLocalMemory,发现并没有存活的实例。文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

图片文章源自JAVA秀-https://www.javaxiu.com/43235.html

以上我们复现了ThreadLocal不正当使用,引起的内存泄漏。demo在这里。文章源自JAVA秀-https://www.javaxiu.com/43235.html

所以我们总结了使用ThreadLocal时会发生内存泄漏的前提条件:文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ①ThreadLocal引用被设置为null,且后面没有set,get,remove操作。文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ②线程一直运行,不停止。(线程池)文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ③触发了垃圾回收。(Minor GC或Full GC)文章源自JAVA秀-https://www.javaxiu.com/43235.html

我们看到ThreadLocal出现内存泄漏条件还是很苛刻的,所以我们只要破坏其中一个条件就可以避免内存泄漏,单但为了更好的避免这种情况的发生我们使用ThreadLocal时遵守以下两个小原则:文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ①ThreadLocal申明为private static final。文章源自JAVA秀-https://www.javaxiu.com/43235.html

    • Private与final 尽可能不让他人修改变更引用,文章源自JAVA秀-https://www.javaxiu.com/43235.html

    • Static 表示为类属性,只有在程序结束才会被回收。文章源自JAVA秀-https://www.javaxiu.com/43235.html

  • ②ThreadLocal使用后务必调用remove方法。文章源自JAVA秀-https://www.javaxiu.com/43235.html

    • 最简单有效的方法是使用后将其移除。文章源自JAVA秀-https://www.javaxiu.com/43235.html

以上。文章源自JAVA秀-https://www.javaxiu.com/43235.html

- END -文章源自JAVA秀-https://www.javaxiu.com/43235.html

欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

已在知识星球更新源码解析如下:文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

全方位,多角度理解ThreadLocal(强烈推荐)文章源自JAVA秀-https://www.javaxiu.com/43235.html

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。文章源自JAVA秀-https://www.javaxiu.com/43235.html

提供近 3W 行代码的 SpringBoot 示例,以及超 6W 行代码的电商微服务项目。文章源自JAVA秀-https://www.javaxiu.com/43235.html

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。文章源自JAVA秀-https://www.javaxiu.com/43235.html

文章源自JAVA秀-https://www.javaxiu.com/43235.html

文章有帮助的话,在看,转发吧。谢谢支持哟 (*^__^*)
文章源自JAVA秀-https://www.javaxiu.com/43235.html

阅读原文文章源自JAVA秀-https://www.javaxiu.com/43235.html

继续阅读
速蛙云 - 极致体验,强烈推荐!!!购买套餐就免费送各大视频网站会员!快速稳定、独家福利社、流媒体稳定解锁!速度快,全球上网、视频、游戏加速、独立IP均支持!基础套餐性价比很高!这里不多说,我一直正在使用,推荐购买:https://www.javaxiu.com/59919.html
weinxin
资源分享QQ群
本站是JAVA秀团队的技术分享社区, 会经常分享资源和教程; 分享的时代, 请别再沉默!
沙海
匿名

发表评论

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定