问题:请讲下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的更多相关文章

  1. java面试一日一题:讲下在什么情况下会发生类加载

    问题:请讲下在什么情况下会发生类加载? 分析:该问题主要考察对java中类加载的知识,什么是类加载,为什么会发生类加载,什么情况下发生类加载? 回答要点: 主要从以下几点去考虑 1.什么是类加载: 2 ...

  2. java面试一日一题:讲下mysql中的undolog

    问题:请讲下mysql中undo log的作用 分析:mysql中有很多日志,例,bin log undo log redo log,要弄清楚这些日志的作用,就要了解这些日志出现的背景及要解决的问题: ...

  3. java面试一日一题:mysql中常用的存储引擎有哪些?

    问题:请讲下mysql中常用的引擎有哪些? 分析:该问题主要考察对mysql存储引擎的理解,及区别是什么? 回答要点: 主要从以下几点去考虑, 1.mysql的存储引擎的基本概念? 2.mysql中常 ...

  4. java面试一日一题:java中垃圾回收算法有哪些

    问题:请讲下在java中有哪些垃圾回收算法 分析:该问题主要考察对java中垃圾回收的算法以及使用场景 回答要点: 主要从以下几点去考虑, 1.GC回收算法有哪些 2.每种算法的使用场景 3.基于垃圾 ...

  5. java面试一日一题:java中的垃圾回收器

    问题:请讲下java中垃圾回收器有哪些? 分析:该问题主要考察hotspot虚拟机下实现的垃圾回收器 回答要点: 主要从以下几点去考虑, 1.垃圾回收器的种类 2.每种垃圾回收器的着重点是什么 前边的 ...

  6. java面试一日一题:请讲下对mysql的理解

    问题:请讲下对mysql的理解 分析:该问题主要考察对mysql的理解,基本概念及sql的执行流程 回答要点: 主要从以下几点去考虑, 1.mysql的整体架构? 2.mysql中每一个组件的作用? ...

  7. java面试一日一题:讲下redo log

    问题:请讲下redo log的作用 分析:mysql中有很多日志,例,binlog undo log redo log,要弄清楚这些日志的作用,就要了解这些日志出现的背景及要解决的问题? 回答要点: ...

  8. java面试一日一题:讲下mysql中的索引

    问题:请讲下mysql中的索引 分析:mysql中有很多索引,要对对这些索引有所掌握,还要弄清楚每种索引的本质? 回答要点: 主要从以下几点去考虑 1.索引的本质是什么 2.mysql的索引分类: 3 ...

  9. java面试一日一题:讲对mysql的MVCC的理解

    问题:请讲下对mysql中MVCC的理解 分析:这个问题要回答的是对MVCC的理解,以及MVCC解决了什么问题这几个方面入手. 回答要点: 主要从以下几点去考虑, 1.什么是MVCC? 2.MVCC用 ...

  10. java面试一日一题:java线程池

    问题:请讲下java中的线程池 分析:在面试中经常问到线程池的问题,要掌握其基本概念,使用方法,注意事项等,引申下tomcat中默认的线程数是多少 回答要点: 主要从以下几点去考虑, 1.为什么要使用 ...

随机推荐

  1. Mybatis-Plus update不存在的数据返回值一定为零?

    MP update不存在的数据返回值一定为零? 本文分为以下几个部分: 前言 验证过程 结论 前言 ​ MP(mybatis-plus),在 MyBatis 的基础上只做增强不做改变,为简化开发.提高 ...

  2. .NET6之MiniAPI(九):基于角色的身份验证和授权

    身份验证是这样一个过程:由用户提供凭据,然后将其与存储在操作系统.数据库.应用或资源中的凭据进行比较. 在授权过程中,如果凭据匹配,则用户身份验证成功,可执行已向其授权的操作. 授权指判断允许用户执行 ...

  3. golang的 CGO 是什么

    CGO是Go(Golang)语言中的一个工具,全称为 "C-Go" 或者 "C for Go". 它是Go标准库的一部分,允许Go代码与C语言代码进行交互. C ...

  4. liunx查看nginx 进程

    ChatGPT4.0国内站点: https://www.weijiwangluo.com/talk 要查看nginx进程,可以使用以下命令: ps -ef | grep nginx 这个命令会列出当前 ...

  5. Dump Rtmp Stream To FLV File (从Rtmp流保存为FLV文件)

    一.准备工作 搭建本地rtmp服务: https://www.cnblogs.com/doudouyoutang/p/6602430.html 获取使用到的库,openssl 和 librtmp 参考 ...

  6. kubernetes的搭建(一)

    集群的搭建 集群的类型 kubunetes的集群类型大致上分为两类: 一主多从和多主多从. 一主多从: 一台master节点和多台node节点,搭建简单,但是有单机故障的风险,适用于测试环境 多主多从 ...

  7. OpenSSL生成加密证书.cer和.pfx

    linux自带openssl,所以在linux平台操作. 先输入: openssl 再输入: genrsa -out private-rsa.key 1024 生成私钥,无密码保护的私钥. 输入: r ...

  8. JSP四个作用域和九个对象

    一.四个作用域 (1)Requset 请求作用域,就是客户端的一次请求 (2)Session 会话作用域,当用户首次访问时,产生一个新的会话,以后服务器就可以记住这个会话状态.生命周期:会话超时,或者 ...

  9. Python str 转 b’二进制串

    用raw_unique_escape来编码无义意的二进制串 bytes(strtext, encoding='raw_unique_escape')

  10. ubuntu 18.04 设置开机自启

    ubuntu 18.04 设置开机自启 背景 为了添加一些自定义的服务,例如autossh. 原文(有删改):https://zhuanlan.zhihu.com/p/98804785 介绍 ubun ...