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.为什么要使用 ...
随机推荐
- Mybatis-Plus update不存在的数据返回值一定为零?
MP update不存在的数据返回值一定为零? 本文分为以下几个部分: 前言 验证过程 结论 前言 MP(mybatis-plus),在 MyBatis 的基础上只做增强不做改变,为简化开发.提高 ...
- .NET6之MiniAPI(九):基于角色的身份验证和授权
身份验证是这样一个过程:由用户提供凭据,然后将其与存储在操作系统.数据库.应用或资源中的凭据进行比较. 在授权过程中,如果凭据匹配,则用户身份验证成功,可执行已向其授权的操作. 授权指判断允许用户执行 ...
- golang的 CGO 是什么
CGO是Go(Golang)语言中的一个工具,全称为 "C-Go" 或者 "C for Go". 它是Go标准库的一部分,允许Go代码与C语言代码进行交互. C ...
- liunx查看nginx 进程
ChatGPT4.0国内站点: https://www.weijiwangluo.com/talk 要查看nginx进程,可以使用以下命令: ps -ef | grep nginx 这个命令会列出当前 ...
- Dump Rtmp Stream To FLV File (从Rtmp流保存为FLV文件)
一.准备工作 搭建本地rtmp服务: https://www.cnblogs.com/doudouyoutang/p/6602430.html 获取使用到的库,openssl 和 librtmp 参考 ...
- kubernetes的搭建(一)
集群的搭建 集群的类型 kubunetes的集群类型大致上分为两类: 一主多从和多主多从. 一主多从: 一台master节点和多台node节点,搭建简单,但是有单机故障的风险,适用于测试环境 多主多从 ...
- OpenSSL生成加密证书.cer和.pfx
linux自带openssl,所以在linux平台操作. 先输入: openssl 再输入: genrsa -out private-rsa.key 1024 生成私钥,无密码保护的私钥. 输入: r ...
- JSP四个作用域和九个对象
一.四个作用域 (1)Requset 请求作用域,就是客户端的一次请求 (2)Session 会话作用域,当用户首次访问时,产生一个新的会话,以后服务器就可以记住这个会话状态.生命周期:会话超时,或者 ...
- Python str 转 b’二进制串
用raw_unique_escape来编码无义意的二进制串 bytes(strtext, encoding='raw_unique_escape')
- ubuntu 18.04 设置开机自启
ubuntu 18.04 设置开机自启 背景 为了添加一些自定义的服务,例如autossh. 原文(有删改):https://zhuanlan.zhihu.com/p/98804785 介绍 ubun ...