java面试一日一题:讲下ThreadLocal
问题:请讲下ThreadLocal
分析:首先要了解ThreadLocal的基本原理;其次要理解ThreadLocal发生内存泄漏的原因;最后ThreadLocal是如何做到线程隔离的
回答要点:
主要从以下几点去考虑,
1、ThreadLocal的基本原理
2、ThreadLocal为什么会发生内存泄漏?
3、ThreadLocal如何做到线程隔离?
ThreadLocal相当于操作线程中局部变量的一个工具类,其过程是通过操作每个线程内部的ThreadLocalMap来实现的,也就是说在每个线程内部都有一个ThreadLocalMap,该map在存储的时候使用的key为ThreadLocal,value为设置的value,其底层是一个Entry数组。 由于每个线程都有一个ThreadLocalMap,在线程A中使用ThreadLocal放入一个value,那么在线程B中使用ThreadLocal获取的时候,一定是获取不到的,因为每个线程有自己独立的ThreadLocalMap,使用ThreadLocal设置的值最终是存储在线程的ThreadLocalMap中的。
既然是一个Map的结构,就会发生冲突,在HashMap中解决hash冲突使用的是链地址法,在ThreadLocalMap中使用的是线性探测法。
重点看下ThreadLocal的几个重点方法,
get()方法
public T get() {
//获得当前线程
Thread t = Thread.currentThread();
//从当前线程中获得其ThreadLocal.ThreadLocalMap变量
ThreadLocalMap map = getMap(t);
if (map != null) {
//这里的this只得是调用该get()方法的对象,也就是一个ThreadLocal的实例,
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
上面的方法做了注释,在使用ThreadLocal的get方法时首先是获得当前调用线程的一个ThreadLocalMap,然后从该Map中获得value。
set()方法
public void set(T value) {
//获得当前线程
Thread t = Thread.currentThread();
//获得当前线程中的ThreadLocal.ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null)
//使用this(也就是ThreadLocal作为key)放入ThreadLocalMap中
map.set(this, value);
else
createMap(t, value);//新建一个ThreadLocalMap并放入值
}
从上面可以看到set方法也是要获得当前线程的ThreadLocalMap对象,然后往该对象中放value,下面看下createMap方法
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
可以看到就是给Thread的threadLocals变量赋值,赋值一个ThreadLocalMap,那么threadLocals肯定是一个ThreadLocal.ThreadLocalMap,看下该变量

通过上面的分析,已经很清晰了ThreadLocal操作的都是当前线程中的变量,和其他线程是没有关系的,所以本身就不存在线程安全的问题;
网上经常看到说ThreadLocal有内存泄漏的风险,那么内存泄漏是如何产生的?

上图,画了下get的一个过程,正常情况下是ThreadLocal.get()-->thread->threadLocalMap-Entry[]-entry(key,value)这样一个查找路径,由于Entry中的key是一个weakReference对象,也就是弱引用,在一次gc的过程中会被回收,那么这时Entry中的key就是null了,再通过上面的线路是找不到的,那么这样一个Entry对象中的value就无法再找到了,但由于value又是强引用,不会被回收,该entry对象就造成了内存泄漏,所以正确的使用方法是在使用完之后调用remove()方法。
最后有个问题,请广大网友解惑,在ThreadLocal中存储的是引用类型的情况下,是如何做到线程隔离的,望解惑,感谢!
其实如果在ThreadLocal中存储得是引用类型,也就是可变对象,那么在一个线程中对value进行改变,在其他线程中渠道的值肯定是改变后的。所以在使用中基本类型是可以随便用的,当使用到了引用类型要注意;
总结:
1、ThreadLocal不是为每个线程创建副本变量,而是由于每个线程中的threadLocals是线程独有的,其他线程无法访问;
2、谨慎的使用引用类型,因为在一个线程中对其值的改变会影响其他线程中值得变化;
3、在使用完,记得remove,防止内存泄漏得风险;

java面试一日一题:讲下ThreadLocal的更多相关文章
- java面试一日一题:讲下在什么情况下会发生类加载
问题:请讲下在什么情况下会发生类加载? 分析:该问题主要考察对java中类加载的知识,什么是类加载,为什么会发生类加载,什么情况下发生类加载? 回答要点: 主要从以下几点去考虑 1.什么是类加载: 2 ...
- java面试一日一题:讲下mysql中的undolog
问题:请讲下mysql中undo log的作用 分析:mysql中有很多日志,例,bin log undo log redo log,要弄清楚这些日志的作用,就要了解这些日志出现的背景及要解决的问题: ...
- java面试一日一题:mysql中常用的存储引擎有哪些?
问题:请讲下mysql中常用的引擎有哪些? 分析:该问题主要考察对mysql存储引擎的理解,及区别是什么? 回答要点: 主要从以下几点去考虑, 1.mysql的存储引擎的基本概念? 2.mysql中常 ...
- java面试一日一题:java中垃圾回收算法有哪些
问题:请讲下在java中有哪些垃圾回收算法 分析:该问题主要考察对java中垃圾回收的算法以及使用场景 回答要点: 主要从以下几点去考虑, 1.GC回收算法有哪些 2.每种算法的使用场景 3.基于垃圾 ...
- java面试一日一题:java中的垃圾回收器
问题:请讲下java中垃圾回收器有哪些? 分析:该问题主要考察hotspot虚拟机下实现的垃圾回收器 回答要点: 主要从以下几点去考虑, 1.垃圾回收器的种类 2.每种垃圾回收器的着重点是什么 前边的 ...
- java面试一日一题:请讲下对mysql的理解
问题:请讲下对mysql的理解 分析:该问题主要考察对mysql的理解,基本概念及sql的执行流程 回答要点: 主要从以下几点去考虑, 1.mysql的整体架构? 2.mysql中每一个组件的作用? ...
- java面试一日一题:讲下redo log
问题:请讲下redo log的作用 分析:mysql中有很多日志,例,binlog undo log redo log,要弄清楚这些日志的作用,就要了解这些日志出现的背景及要解决的问题? 回答要点: ...
- java面试一日一题:讲下mysql中的索引
问题:请讲下mysql中的索引 分析:mysql中有很多索引,要对对这些索引有所掌握,还要弄清楚每种索引的本质? 回答要点: 主要从以下几点去考虑 1.索引的本质是什么 2.mysql的索引分类: 3 ...
- java面试一日一题:讲对mysql的MVCC的理解
问题:请讲下对mysql中MVCC的理解 分析:这个问题要回答的是对MVCC的理解,以及MVCC解决了什么问题这几个方面入手. 回答要点: 主要从以下几点去考虑, 1.什么是MVCC? 2.MVCC用 ...
- java面试一日一题:java线程池
问题:请讲下java中的线程池 分析:在面试中经常问到线程池的问题,要掌握其基本概念,使用方法,注意事项等,引申下tomcat中默认的线程数是多少 回答要点: 主要从以下几点去考虑, 1.为什么要使用 ...
随机推荐
- protoc-gen-go: error:inconsistent package names: , prototest
如果你已经安装proto ,以及go生成proto插件.但还是报这种错误,请看一下是否 protoc --go_out=./ *.proto 指令打错了
- UINavgationBar事件穿透
一.事件起因 最近在开发一版本的需求中,遇到一个问题,需要在一个ViewController的顶部,UINavgationBar的下面放置一个View,这个View需要能够正常收到事件 将我们的Vie ...
- 一个或多个C文件编译KO
参考文档:.c文件如何编译为ko的MAKEFILE文件编写 - young525 - 博客园 (cnblogs.com) 文档组织结构 header目录:存放头文件 source目录:存放源文件 单个 ...
- 生成树协议(STP)基本知识及实验(使用eNSP)
1.基本知识--摘至<网络之路--交换专题> (1)生成树的作用:在链路层消除环路上可能出现的广播风暴. (2)生成树的工作由三部分组成:选举过程.拓扑计算.端口行为确定. 选举过程:在二 ...
- 绘图与可视化--pandas中的绘图函数
matplotlib API函数都位于matplotlib.pyplot模块中. 本节代码中引入的约定为:import matplotlib.pyplot as plt numpy库也会用到,约定:i ...
- 程序员面试金典-面试题 01.03. URL化
题目: URL化.编写一种方法,将字符串中的空格全部替换为%20.假定该字符串尾部有足够的空间存放新增字符,并且知道字符串的"真实"长度.(注:用Java实现的话,请使用字符数组实 ...
- The remote name could not be resolved
HTTP The remote name could not be resolved HTTP Status:NameResolutionFailure
- vm ware 虚拟WIN XP 时卡
主机是I7 9700,SN750 NVME 1T SSD, 32G 内存,VM WARE 14,理论上虚拟WIN XP 不会卡,但实际情况就是很卡. 虚拟WIN7 ,WIN 10,UBUNTU LIN ...
- Jmeter进行HTTPS接口压测及SSL证书验证
一.前言 使用JMeter压测HTTPS接口比较简单,只需要预先处理SSL证书认证,后面就是压测HTTP接口的通用步骤. HTTPS连接证书来验证浏览器和WEB服务器之间的连接.通过HTTP连接时,服 ...
- Go 语言中的异常处理简单实践 panic、recover【GO 基础】
〇.Go 中的异常处理简介 Golang 没有结构化异常,使用 panic 抛出错误,recover 捕获错误. panic.recover 参数类型为 interface{},因此可抛出任何类型对象 ...