呵呵,你这是在背面试题吧?ThreadLocal使用中会有那些坑?
点击关注下方公众号,架构师全套资料 都在这里
0、2T架构师学习资料干货分享
来源:blog.csdn.net/zzg1229059735/article/details/82715741
上一篇:清华大学:2021 元宇宙研究报告!
本次给大家介绍重要的工具ThreadLocal。讲解内容如下,同时介绍什么场景下发生内存泄漏,如何复现内存泄漏,如何正确使用它来避免内存泄漏。
ThreadLocal是什么?有哪些用途?
ThreadLocal如何使用
ThreadLocal原理
ThreadLocal使用有哪些坑及注意事项
ThreadLocal是什么?有哪些用途?
首先介绍Thread类中属性threadLocals:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
我们发现Thread并没有提供成员变量threadLocals的设置与访问的方法,那么每个线程的实例threadLocals参数我们如何操作呢?这时我们的主角:ThreadLocal就登场了。
所以有那么一句总结:ThreadLocal是线程Thread中属性threadLocals的管理者。
也就是说我们对于ThreadLocal的get, set,remove的操作结果都是针对当前线程Thread实例的threadLocals存,取,删除操作。类似于一个开发者的任务,产品经理左右不了,产品经理只能通过技术leader来给开发者分配任务。下面再举个栗子,进一步说明他们之间的关系:
每个人都一张银行卡
每个人每张卡都有一定的余额。
每个人获取银行卡余额都必须通过该银行的管理系统。
每个人都只能获取自己卡持有的余额信息,他人的不可访问。
映射到我们要说的ThreadLocal
card类似于Thread
card余额属性,卡号属性等类似于Treadlocal内部属性集合threadLocals,搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。
cardManager类似于ThreadLocal管理类
那ThreadLocal有哪些应用场景呢?
其实我们无意间已经时时刻刻在使用ThreadLocal提供的便利,如果说多数据源的切换你比较陌生,那么spring提供的声明式事务就再熟悉不过了,我们在研发过程中无时无刻不在使用,而spring声明式事务的重要实现基础就是ThreadLocal,只不过大家没有去深入研究spring声明式事务的实现机制。后面有机会我会给大家介绍spring声明式事务的原理及实现机制。
原来ThreadLocal这么强大,但应用开发者使用较少,同时有些研发人员对于ThreadLocal内存泄漏,等潜在问题,不敢试用,恐怕这是对于ThreadLocal最大的误解,后面我们将会仔细分析,只要按照正确使用方式,就没什么问题。如果ThreadLocal存在问题,岂不是spring声明式事务是我们程序最大的潜在危险吗?搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。
ThreadLocal如何使用
为了更直观的体会ThreadLocal的使用我们假设如下场景
我们给每个线程生成一个ID。
一旦设置,线程生命周期内不可变化。
容器活动期间不可以生成重复的ID
我们创建一个ThreadLocal管理类:
测试程序如下:我们同一个线程不断get,测试id是否变化,同时测试完成后我们就将其释放掉。搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。
在主程序中我们开启多个线程测试不通线程之间是否会影响
不出意外我们的结果为:
结果:确实是不同线程间id不同,相同线程id相同。
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的过程也类似:搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。
注意:ThreadLocal中可以直接t.threadLocals是因为Thread与ThreadLocal在同一个包下,同样Thread可以直接访问ThreadLocal.ThreadLocalMap threadLocals = null;来进行声明属性。
ThreadLocal使用有哪些坑及注意事项
我经常在网上看到骇人听闻的标题,ThreadLocal导致内存泄漏,这通常让一些刚开始对ThreadLocal理解不透彻的开发者,不敢贸然使用。越不用,越陌生。这样就让我们错失了更好的实现方案,所以敢于引入新技术,敢于踩坑,才能不断进步。
我们来看下为什么说ThreadLocal会引起内存泄漏,什么场景下会导致内存泄漏?搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。
先回顾下什么叫内存泄漏,对应的什么叫内存溢出
①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 IDpublic ThreadLocal<List<Object>> threadId = new ThreadLocal<List<Object>>() {@Overrideprotected 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 necessarypublic List<Object> get() {return threadId.get();}// remove currentidpublic 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 <pid>
然后我们借助MAT可视化分析工具,来查看对内存,分析对象实例的存活状态:搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。
首先打开我们工具提示我们的内存泄漏分析:
这里我们可以确定的是ThreadLocalMap实例的Entry.value是没有被回收的。
最后我们要确定Entry.key是否还在?打开Dominator Tree,搜索我们的ThreadLocalMemory,发现并没有存活的实例。
以上我们复现了ThreadLocal不正当使用,引起的内存泄漏。demo在这里。
所以我们总结了使用ThreadLocal时会发生内存泄漏的前提条件:
①ThreadLocal引用被设置为null,且后面没有set,get,remove操作。
②线程一直运行,不停止。(线程池)
③触发了垃圾回收。(Minor GC或Full GC)
我们看到ThreadLocal出现内存泄漏条件还是很苛刻的,所以我们只要破坏其中一个条件就可以避免内存泄漏,单但为了更好的避免这种情况的发生我们使用ThreadLocal时遵守以下两个小原则:
①ThreadLocal申明为private static final。
Private与final 尽可能不让他人修改变更引用,
Static 表示为类属性,只有在程序结束才会被回收。
②ThreadLocal使用后务必调用remove方法。
最简单有效的方法是使用后将其移除。
呵呵,你这是在背面试题吧?ThreadLocal使用中会有那些坑?相关推荐
- 【答阿里寒冬面试题】呵呵,大神的面试题就是好!
前言 今天微博看到了寒冬大神的面试题,觉得挺有意思的,这里就做一点解答 http://weibo.com/1196343093/Bhj510t50 谈谈你对CSS布局的理解 讲讲输入完网址按下回车,到 ...
- 还在背面试题?听小米面试官怎么说?【面试官教你如何对线面试官】
我们本章要学习的内容有软件测试行业现状以及发展的前景,然后测试的职业发展与规划,还有软件测试的定义.目的以及对象,还有很重要的软件测试原则. 1.测试行业行业现状与前景 2.测试职业发展与规划 3.测 ...
- 【测试面经】软件测试面试题大全,软件测试必问必背面试题,敢说会70%就可以轻松拿offer......
目录:导读 前言 一.测试面试基础题 二.测试实战面试题 三.测试基础知识点 四.总结 前言 大部分人学软件测试的从业者,在找工作的同时,会因为软件测试面试题挡在门前. --跳槽最重要的一步自然是面试 ...
- JAVA必背面试题和项目面试通关要点
一 数据库 1.常问数据库查询.修改(SQL查询包含筛选查询.聚合查询和链接查询和优化问题,手写SQL语句,例如四个球队比赛,用SQL显示所有比赛组合:举例2:选择重复项,然后去掉重复项:) 数据库里 ...
- Java 200+ 面试题补充 ThreadLocal 模块
让我们每天都有进步,老王带你打造最全的 Java 面试清单,认真把一件事做到极致. 本文是前文<Java 最常见的 200+ 面试题>的第一个补充模块. 1.ThreadLocal 是什么 ...
- 【Java面试题】docker启动失败原因
对于面试大厂的朋友,一些建议 阿里 阿里面试官一般都是P7/P8岗,对标到普通互联网公司相当于就是技术专家那种类型!他们对于没有经验的毕业生面试问的比较浅一点,大多数问题问的集合.锁.JVM调优,线程 ...
- Java经典面试题整理及答案详解(二)
简介: 现在找工作,背面试题是一方面,但更重要的就是得把这些代码的前后因果关系了解的非常清楚,还是要多写代码多练习. 1. 使用InputStream用什么方法? 答:Read()方法 2. 在JQu ...
- 杭州 3~5年 前端面经,高频面试题总结
大家好,我是若川.假期归来,国庆期间没有更文,不用想每天发什么文章,不用担心阅读量,其实感觉挺好. 最近组织了源码共读活动<1个月,200+人,一起读了4周源码>,已经有超100+人提交了 ...
- 三年Java开发,java基础常问面试题
一.首先本职工作一定要做好做精 本人之前在干兼职的时候,也忽视过本职工作,从而导致自己落后平均技术水平,虽然之后迎头赶上,但这不能不算是个遗憾.前在接一些活的时候就感觉技术的重要性了,如果当年我技术再 ...
- java基础常问面试题,面试必问
一.首先本职工作一定要做好做精 本人之前在干兼职的时候,也忽视过本职工作,从而导致自己落后平均技术水平,虽然之后迎头赶上,但这不能不算是个遗憾.前在接一些活的时候就感觉技术的重要性了,如果当年我技术再 ...
最新文章
- 《天气一点通》隐私策略
- python中options设置_如何在AngularJS的ng-options中设置value属性?
- Hibernate学习之Hibernate注解总结
- [Android] Android学习手记(二)
- Hadoop生态Flume(一)介绍
- Java之Socket与HTTP区别
- jquery-加入购物车动画
- 2.0版本中如何取得当前的控制器和方法
- sql always on_Always On可用性组中具有发布者数据库SQL复制
- 在计算机中添加用户名和密码是多少,在win7系统中添加网络打印机提示输入用户名和密码的解决方案...
- hdu1247 字典树
- IIS7程序发布后 之 报图表处理程序配置 [c:\TempImageFiles\] 中的临时目录无效
- linux系统编译Q,Linux下安装qBittorrent,开启24小时挂机BT下载
- word07 去掉标题前的黑点
- linux7 域名解析,CentOS 7之DNS域名解析
- 3种Flink State Backed| 你该用哪个?
- 谭浩强C++ 第七章
- 【iOS地图导航】“步行导航”及“HUD导航”的实现,demo+高清源码,大家按需自取~~
- gpa计算器java代码_GP中的GPA计算器帮助
- 对一级标题二级标题进行排序
热门文章
- 504 Gateway Time-out 错误处理记录
- clang命令编译c++程序时报错
- 启用Exchange邮箱审核后使用命令Search-MailboxAuditLog返回结果为空
- 蚂蚁分类信息系统5.8 解决手机端新闻详情页面图片不自动缩放问题
- Neutron中的网络I/O虚拟化
- 《51单片机应用开发从入门到精通》——2.8 用外部中断控制灯闪烁
- cannot register datanode 0.0.0.0:1002,because current license expired time is
- android系统文件的权限
- An Overview of MITRE Cyber Situational Awareness Solutions
- Android项目Tab类型主界面大总结