ThreadLocal是什么?有哪些用途?你了解多少?

测试程序如下:我们同一个线程不断get,测试id是否变化,同时测试完成后我们就将其释放掉。

在主程序中我们开启多个线程测试不通线程之间是否会影响

不出意外我们的结果为:

结果:确实是不同线程间id不同,相同线程id相同。

3.ThreadLocal原理


①ThreadLocal类结构及方法解析:

上图可知:ThreadLocal三个方法get, set , remove以及内部类ThreadLocalMap

②ThreadLocal及Thread之间的关系:

从这张图我们可以直观的看到Thread中属性threadLocals,作为一个特殊的Map,它的key值就是我们ThreadLocal实例,而value值这是我们设置的值。

③ThreadLocal的操作过程:

我们以get方法为例:

其中getMap(t)返回的就上当前线程的threadlocals,如下图,然后根据当前ThreadLocal实例对象作为key获取ThreadLocalMap中的value,如果首次进来这调用setInitialValue()

set的过程也类似:

注意:ThreadLocal中可以直接t.threadLocals是因为Thread与ThreadLocal在同一个包下,同样Thread可以直接访问ThreadLocal.ThreadLocalMap threadLocals = null;来进行声明属性。另外,欢迎关注公众号Java笔记虾,后台回复“后端面试”,送你一份面试题宝典!

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


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

我们来看下为什么说ThreadLocal会引起内存泄漏,什么场景下会导致内存泄漏?

先回顾下什么叫内存泄漏,对应的什么叫内存溢出

  • Memory overflow:内存溢出,没有足够的内存提供申请者使用。

  • Memory leak:内存泄漏,程序申请内存后,无法释放已申请的内存空间,内存泄漏的堆积终将导致内存溢出。

显然是TreadLocal在不规范使用的情况下导致了内存没有释放。

红框里我们看到了一个特殊的类WeakReference,同样这个类,应用开发者也同样很少使用,这里简单介绍下吧

既然WeakReference在下一次gc即将被回收,那么我们的程序为什么没有出问题呢?

①所以我们测试下弱引用的回收机制:

这一种存在强引用不会被回收。

这里没有强引用将会被回收。

上面演示了弱引用的回收情况,下面我们看下ThreadLocal的弱引用回收情况。

②ThreadLocal的弱引用回收情况

如上图所示,我们在作为key的ThreadLocal对象没有外部强引用,下一次gc必将产生key值为null的数据,若线程没有及时结束必然出现,一条强引用链Threadref–>Thread–>ThreadLocalMap–>Entry,所以这将导致内存泄漏

下面我们模拟复现ThreadLocal导致内存泄漏:

1.为了效果更佳明显我们将我们的treadlocals的存储值value设置为1万字符串的列表:

class ThreadLocalMemory {

// Thread local variable containing each thread’s ID

public ThreadLocal<List> threadId = new ThreadLocal<List>() {

@Override

protected List initialValue() {

List list = new ArrayList();

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 get() {

return threadId.get();

}

// remove currentid

public void remove() {

threadId.remove();

}

}

测试代码如下:

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呢?

我们可以借助jdk提供的一些命令dump当前堆内存,命令如下:

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

然后我们借助MAT可视化分析工具,来查看对内存,分析对象实例的存活状态:

首先打开我们工具提示我们的内存泄漏分析:

这里我们可以确定的是ThreadLocalMap实例的Entry.value是没有被回收的。

最后我们要确定Entry.key是否还在?打开Dominator Tree,搜索我们的ThreadLocalMemory,发现并没有存活的实例。

以上我们复现了ThreadLocal不正当使用,引起的内存泄漏。

文中源码:

https://github.com/z-one/basictest/tree/master/src/main/java/com/spring/test/threadlocal

所以我们总结了使用ThreadLocal时会发生内存泄漏的前提条件:

  • ThreadLocal引用被设置为null,且后面没有set,get,remove操作。

  • 线程一直运行,不停止。(线程池)

  • 触发了垃圾回收。(Minor GC或Full GC)

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

①ThreadLocal申明为private static final。

  • Private与final 尽可能不让他人修改变更引用,

  • Static 表示为类属性,只有在程序结束才会被回收。

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注Java获取)

img

总结

本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!

MySQL50道高频面试题整理:

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
-community.csdnimg.cn/images/e5c14a7895254671a72faed303032d36.jpg" alt=“img” style=“zoom: 33%;” />

总结

本文从基础到高级再到实战,由浅入深,把MySQL讲的清清楚楚,明明白白,这应该是我目前为止看到过最好的有关MySQL的学习笔记了,我相信如果你把这份笔记认真看完后,无论是工作中碰到的问题还是被面试官问到的问题都能迎刃而解!

MySQL50道高频面试题整理:

[外链图片转存中…(img-2lZ3zdAm-1713433546906)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ThreadLocal类有一个实例变量threadLocals,它的类型是ThreadLocal.ThreadLocalMap。这意味着每个线程都有自己的ThreadLocalMap。ThreadLocal的作用是管理线程Thread中的属性threadLocals。ThreadLocal通过提供get和set方法,使得每个线程都可以独立地访问和修改自己的ThreadLocalMap。 ThreadLocalMap中存储了线程局部变量的键值对,其中键是ThreadLocal对象,值是线程局部变量的值。这样,每个线程都可以独立地存储和获取自己的线程局部变量。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [面试官:听说你看过ThreadLocal源码?我来瞅瞅?](https://blog.csdn.net/l18848956739/article/details/106122096)[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: 33.333333333333336%"] - *2* [ThreadLocal_ThreadLocal源码分析_](https://download.csdn.net/download/weixin_42666807/25851858)[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: 33.333333333333336%"] - *3* [13]ThreadLocal是什么?有哪些用途?你了解多少?](https://blog.csdn.net/wu6cfp38/article/details/119494334)[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: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值