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

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. HttpClient basic authentication
  2. noip模拟赛 abcd
  3. Linux破解系统管理员密码
  4. open函数返回-1_python常用函数大总结
  5. python编写学生选课系统程序_python面向对象编程小程序- 选课系统
  6. Java笔试面试题001
  7. matlab2c使用c++实现matlab函数系列教程-ones函数
  8. 进程外session(session保存在sqlserver)
  9. Google 出品的 Java 编码规范,强烈推荐,权威又科学!
  10. kitti数据集介绍和使用--待整理
  11. web漏洞扫描器原理_黑客秘籍:基于WAF日志的扫描器检测实践
  12. php 分享微博,PHP调用微博接口实现微博登录
  13. 中秋节快乐ooooo
  14. logstash String转data,@timestamp转化为东八区时间
  15. Graphite 介绍
  16. Delphi 复制文本框内容到剪贴板
  17. 大促系统全流量压测及稳定性保证——京东交易架构分享(含PPT)
  18. 0x8(0x80070035找不到网络路径)
  19. 银行精准营销的概念讲解
  20. IntelliJ IDEA安装教程

热门文章

  1. EXCEL如何新建自定义单元格以及样式怎么设置
  2. Creating Apps With Material Design —— Defining Shadows and Clipping Views
  3. Java学习笔记(三):数组
  4. atitit.研发管理--标准化流程总结---java开发环境与项目部署环境的搭建工具包总结...
  5. Shared File System Master Slave 全配置以及测试
  6. 科技四巨头展望:战线***升级 软硬界限模糊
  7. 用 JNI 进行 Java 编程(1)
  8. 「leetcode」450. 删除二叉搜索树中的节点:【递归】【迭代】详解
  9. Lightroom Classic 教程,如何将照片从 Lightroom 移至Ps,在 Ps 中为照片应用滤镜?
  10. InDesign 教程,如在使用的不同类型的框架?