Java ThreadLocal解析
简介
ThreadLocal 类似局部变量,解决了单个线程维护自己线程内的变量值(存、取、删),让线程之间的数据进行隔离。(InheritableThreadLocal 特例)
这里涉及三个类,Thread、ThreadLocal、ThreadLocalMap
源码解析

- Thread 中有一个 ThreadLocal.ThreadLocalMap 类型的变量 threadLocals。因为ThreadLocalMap变量是跟线程绑定的,所以不存在多线程共享变量之间的并发问题,所以ThreadLocal也就是线程安全的变量。
- ThreadLocalMap 是 ThreadLocal 的一个内部静态类,没有继承java.util.Map,定义了一个Entry[]变量,通过Entry的get()方法作为key,value属性作为值来实现一个类似Map的操作
- Entry 是 ThreadLocalMap 的一个内部静态类,继承WeakReference<ThreadLocal<?>>,并且定义了一个变量value(Object类型)
- ThreadLocal 内部封装了getMap()、Set()、Get()、Remove()4个核心方法,用于操作ThreadLocalMap
- 通过getMap()获取每个子线程Thread持有自己的ThreadLocalMap实例,因此它们是不存在并发竞争的
- ThreadLocalMap中Entry[]数组存储数据,初始化长度16,大于等于3/4阈值,就进行2倍扩容。
- ThreadLocalMap中Entry的key是对ThreadLocal的弱引用,当主线程抛弃掉ThreadLocal对象时,垃圾收集器会忽略这个key的引用而清理掉ThreadLocal对象, 防止了内存泄漏。
- 散列算法-魔数0x61c88647,利用一定算法实现了元素的完美散列
看源码可以得出,set、get、remove操作的都是ThreadLocalMap,key为当前线程,value为线程局部变量缓存值
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
问题
不调用remove会内存溢出吗?
大部分场景下是不会的,少数场景才会。
运行时,会在栈中产生两个引用,指向堆中相应的对象。
可以看到,ThreadLocalMap使用ThreadLocal的弱引用作为key,这样一来,假设当ThreadLocal ref和ThreadLocal之间的强引用断开时,即ThreadLocal ref被置为null,下一次GC时,threadLocal对象势必会被回收。
这样,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,比如使用线程池,线程使用完成之后会被放回线程池中,不会被销毁,这些key为null的Entry的value就会一直存在一条强引用链:
Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
参考资料
https://www.cnblogs.com/dennyzhangdd/p/7978455.html ThreadLocal终极源码剖析
https://liwx2000.iteye.com/blog/1774169 ThreadLocal会内存溢出吗
https://www.jianshu.com/p/cdb2ea3792b5 深入理解Java弱引用
Java ThreadLocal解析的更多相关文章
- JDK ThreadLocal解析
Java ThreadLocal解析 ThreadLocal 线程本地变量, 线程私有, 在 Thread 类中用 ThreadLocal.ThreadLocalMap threadLocals 以数 ...
- java基础解析系列(七)---ThreadLocal原理分析
java基础解析系列(七)---ThreadLocal原理分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)-- ...
- java基础解析系列(八)---fail-fast机制及CopyOnWriteArrayList的原理
fail-fast机制及CopyOnWriteArrayList的原理 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列( ...
- java基础解析系列(九)---String不可变性分析
java基础解析系列(九)---String不可变性分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系列(二)---In ...
- java基础解析系列(十)---ArrayList和LinkedList源码及使用分析
java基础解析系列(十)---ArrayList和LinkedList源码及使用分析 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder jav ...
- java基础解析系列(十一)---equals、==和hashcode方法
java基础解析系列(十一)---equals.==和hashcode方法 目录 java基础解析系列(一)---String.StringBuffer.StringBuilder java基础解析系 ...
- 并发之线程封闭与ThreadLocal解析
并发之线程封闭与ThreadLocal解析 什么是线程封闭 实现一个好的并发并非易事,最好的并发代码就是尽量避免并发.而避免并发的最好办法就是线程封闭,那什么是线程封闭呢? 线程封闭(thread c ...
- Java Sax解析
一. Java Sax解析是按照xml文件的顺序一步一步的来解析,在解析xml文件之前,我们要先了解xml文件的节点的种类,一种是ElementNode,一种是TextNode.如下面的这段boo ...
- Java XML解析工具 dom4j介绍及使用实例
Java XML解析工具 dom4j介绍及使用实例 dom4j介绍 dom4j的项目地址:http://sourceforge.net/projects/dom4j/?source=directory ...
随机推荐
- SpringCloud-Gateway 网关路由、断言、过滤
Gateway 简介 是什么? Spring Cloud 全家桶中有个很重要的组件:网关.在 1.x 版本中使用的是 Zuul 网关,但是到了 2.x,由于Zuul的升级不断跳票,Spring Clo ...
- Python常见报错 - 使用openpyxl模块时出现错误: zipfile.BadZipFile: File is not a zip file
背景 在pycharm项目下,有一个data.xlsx,主要用来存放接口测试用例数据的 要通过openpyxl库去读取data.xlsx,方法: openpyxl.load_workbook(path ...
- 基于 HTML5 WebGL 的污水处理厂泵站自控系统
前言 一道残阳铺水中,半江瑟瑟半江红.随着城市建设的迅速发展,每年都有大量新建管网水管通水运行.城市中有大量的排水设备,形成相应的城市排水系统,排水系统由检查井.排水泵站.污水处理厂.雨水口.排放口等 ...
- 在项目中部署redis的读写分离架构(包含节点间认证口令)
#### 在项目中部署redis的读写分离架构(包含节点间认证口令) ##### 1.配置过程 --- 1.此前就是已经将redis在系统中已经安装好了,redis utils目录下,有个redis ...
- openssl 查看证书
查看证书 # 查看KEY信息 > openssl rsa -noout -text -in myserver.key # 查看CSR信息 > openssl req -noout -tex ...
- QtConcurrent::run 运行类的成员函数
https://stackoverflow.com/questions/2152355/is-it-possible-to-use-qtconcurrentrun-with-a-function-me ...
- 实用的linux 命令(上)
今天介绍几个我常用的Linux 命令,每个命令这里只介绍其常用参数. 对于每个Linux 命令都可以使用man + 命令名称,查看其完整使用方法. 0,man man 命令是一个非常有用的命令,当你不 ...
- 《Arduino实战》——2.4 反应速度计:谁真正拥有最快的反应时间
本节书摘来异步社区<Arduino实战>一书中的第2章,第2.4节,作者:[美]Martin Evans ,Joshua Noble ,Jordan Hochenbaum,更多章节内容可以 ...
- jQuery里面click、this事件遇到(Django模型里for)相同的id名和class名想获取值
遇到的原型是这样的!下面我把它简化一下; click事件: 在浏览器里面只能获取横线上面的值,和下面的第一个值!! 这是因为id等级比class高,而且js要求id不能重复! 当 转载于:https: ...
- Java泛型和编译优化的一个例子
public class Main { public static void main(String[] args) { ArrayList<String> strList = new A ...