点击关注下方公众号,架构师全套资料 都在这里

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来给开发者分配任务。下面再举个栗子,进一步说明他们之间的关系:

  1. 每个人都一张银行卡

  2. 每个人每张卡都有一定的余额。

  3. 每个人获取银行卡余额都必须通过该银行的管理系统。

  4. 每个人都只能获取自己卡持有的余额信息,他人的不可访问。

映射到我们要说的ThreadLocal

  1. card类似于Thread

  2. card余额属性,卡号属性等类似于Treadlocal内部属性集合threadLocals,搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。

  3. cardManager类似于ThreadLocal管理类

那ThreadLocal有哪些应用场景呢?

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

原来ThreadLocal这么强大,但应用开发者使用较少,同时有些研发人员对于ThreadLocal内存泄漏,等潜在问题,不敢试用,恐怕这是对于ThreadLocal最大的误解,后面我们将会仔细分析,只要按照正确使用方式,就没什么问题。如果ThreadLocal存在问题,岂不是spring声明式事务是我们程序最大的潜在危险吗?搜索公众号互联网架构师回复“2T”,送你一份惊喜礼包。

ThreadLocal如何使用

为了更直观的体会ThreadLocal的使用我们假设如下场景

  1. 我们给每个线程生成一个ID。

  2. 一旦设置,线程生命周期内不可变化。

  3. 容器活动期间不可以生成重复的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使用中会有那些坑?相关推荐

  1. 【答阿里寒冬面试题】呵呵,大神的面试题就是好!

    前言 今天微博看到了寒冬大神的面试题,觉得挺有意思的,这里就做一点解答 http://weibo.com/1196343093/Bhj510t50 谈谈你对CSS布局的理解 讲讲输入完网址按下回车,到 ...

  2. 还在背面试题?听小米面试官怎么说?【面试官教你如何对线面试官】

    我们本章要学习的内容有软件测试行业现状以及发展的前景,然后测试的职业发展与规划,还有软件测试的定义.目的以及对象,还有很重要的软件测试原则. 1.测试行业行业现状与前景 2.测试职业发展与规划 3.测 ...

  3. 【测试面经】软件测试面试题大全,软件测试必问必背面试题,敢说会70%就可以轻松拿offer......

    目录:导读 前言 一.测试面试基础题 二.测试实战面试题 三.测试基础知识点 四.总结 前言 大部分人学软件测试的从业者,在找工作的同时,会因为软件测试面试题挡在门前. --跳槽最重要的一步自然是面试 ...

  4. JAVA必背面试题和项目面试通关要点

    一 数据库 1.常问数据库查询.修改(SQL查询包含筛选查询.聚合查询和链接查询和优化问题,手写SQL语句,例如四个球队比赛,用SQL显示所有比赛组合:举例2:选择重复项,然后去掉重复项:) 数据库里 ...

  5. Java 200+ 面试题补充 ThreadLocal 模块

    让我们每天都有进步,老王带你打造最全的 Java 面试清单,认真把一件事做到极致. 本文是前文<Java 最常见的 200+ 面试题>的第一个补充模块. 1.ThreadLocal 是什么 ...

  6. 【Java面试题】docker启动失败原因

    对于面试大厂的朋友,一些建议 阿里 阿里面试官一般都是P7/P8岗,对标到普通互联网公司相当于就是技术专家那种类型!他们对于没有经验的毕业生面试问的比较浅一点,大多数问题问的集合.锁.JVM调优,线程 ...

  7. Java经典面试题整理及答案详解(二)

    简介: 现在找工作,背面试题是一方面,但更重要的就是得把这些代码的前后因果关系了解的非常清楚,还是要多写代码多练习. 1. 使用InputStream用什么方法? 答:Read()方法 2. 在JQu ...

  8. 杭州 3~5年 前端面经,高频面试题总结

    大家好,我是若川.假期归来,国庆期间没有更文,不用想每天发什么文章,不用担心阅读量,其实感觉挺好. 最近组织了源码共读活动<1个月,200+人,一起读了4周源码>,已经有超100+人提交了 ...

  9. 三年Java开发,java基础常问面试题

    一.首先本职工作一定要做好做精 本人之前在干兼职的时候,也忽视过本职工作,从而导致自己落后平均技术水平,虽然之后迎头赶上,但这不能不算是个遗憾.前在接一些活的时候就感觉技术的重要性了,如果当年我技术再 ...

  10. java基础常问面试题,面试必问

    一.首先本职工作一定要做好做精 本人之前在干兼职的时候,也忽视过本职工作,从而导致自己落后平均技术水平,虽然之后迎头赶上,但这不能不算是个遗憾.前在接一些活的时候就感觉技术的重要性了,如果当年我技术再 ...

最新文章

  1. 《天气一点通》隐私策略
  2. python中options设置_如何在AngularJS的ng-options中设置value属性?
  3. Hibernate学习之Hibernate注解总结
  4. [Android] Android学习手记(二)
  5. Hadoop生态Flume(一)介绍
  6. Java之Socket与HTTP区别
  7. jquery-加入购物车动画
  8. 2.0版本中如何取得当前的控制器和方法
  9. sql always on_Always On可用性组中具有发布者数据库SQL复制
  10. 在计算机中添加用户名和密码是多少,在win7系统中添加网络打印机提示输入用户名和密码的解决方案...
  11. hdu1247 字典树
  12. IIS7程序发布后 之 报图表处理程序配置 [c:\TempImageFiles\] 中的临时目录无效
  13. linux系统编译Q,Linux下安装qBittorrent,开启24小时挂机BT下载
  14. word07 去掉标题前的黑点
  15. linux7 域名解析,CentOS 7之DNS域名解析
  16. 3种Flink State Backed| 你该用哪个?
  17. 谭浩强C++ 第七章
  18. 【iOS地图导航】“步行导航”及“HUD导航”的实现,demo+高清源码,大家按需自取~~
  19. gpa计算器java代码_GP中的GPA计算器帮助
  20. 对一级标题二级标题进行排序

热门文章

  1. 504 Gateway Time-out 错误处理记录
  2. clang命令编译c++程序时报错
  3. 启用Exchange邮箱审核后使用命令Search-MailboxAuditLog返回结果为空
  4. 蚂蚁分类信息系统5.8 解决手机端新闻详情页面图片不自动缩放问题
  5. Neutron中的网络I/O虚拟化
  6. 《51单片机应用开发从入门到精通》——2.8 用外部中断控制灯闪烁
  7. cannot register datanode 0.0.0.0:1002,because current license expired time is
  8. android系统文件的权限
  9. An Overview of MITRE Cyber Situational Awareness Solutions
  10. Android项目Tab类型主界面大总结