当前位置:网站首页>为什么 ThreadLocal 可以做到线程隔离?
为什么 ThreadLocal 可以做到线程隔离?
2022-07-29 13:58:00 【刘水镜】
对于 ThreadLocal 我们都不陌生,它的作用如同它的名字——用于存放「线程本地」变量。
先通过一个小例子感受一下:
private static final ThreadLocal<String> threadLocal = new ThreadLocal<>();
public static void main(String[] args) throws Throwable {
Thread threadOne = new Thread(()->{
threadLocal.set("ThreadOne:" + Thread.currentThread().getName());
log.info("线程 One 本地变量值为:{}", threadLocal.get());
threadLocal.remove();
log.info("线程 One remove 后本地变量值为:{}", threadLocal.get());
});
Thread threadTwo = new Thread(()->{
threadLocal.set("ThreadTwo:" + Thread.currentThread().getName());
log.info("线程 Two 本地变量值为:{}", threadLocal.get());
});
threadOne.start();
threadTwo.start();
}运行结果:
线程 One 本地变量值为:ThreadOne:Thread-0
线程 One remove 后本地变量值为:null
线程 Two 本地变量值为:ThreadTwo:Thread-1OK,从效果上看,ThreadLocal 确实是线程隔离的,那么,它是如何做到线程隔离的呢?下面我们扒一扒源码,看看它是如何做到的:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}set() 方法的逻辑如下:
- 获取当前线程
- 根据当前线程获取一个 ThreadLocalMap 对象
- 如果 map 不为 null 则保存
- 如果 map 为 null 则创建一个 map
getMap() 和 createMap() 方法都干了啥呢?我们点进去看:
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}进入到两个方法内部后发现,不管执行哪个分支,最终是把值保存到了当前线程的 threadLocals 属性中。
查看 Thread 类的源码,你会发现类中定义了一个 threadLocals 属性,且初始值为 null,其类型为 ThreadLocal.ThreadLocalMap。
public class Thread implements Runnable {
// ...
ThreadLocal.ThreadLocalMap threadLocals = null;
// ...
}到此,我们发现了,原来 ThreadLocal 就是把我们要传递的对象放到了当前线程的 threadLocals 属性中。也就是说每个线程在用 ThreadLocal 保存对象时,其实就是将对象放到了当前线程实例对象的 threadLocals 属性里面。这样一来线程之间自然就是互相独立的啦。
再看看 get() 方法:
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();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}ThreadLocal 的 get() 方法其实和 set() 方法逻辑很相似,先从当前线程的 threadLocals 属性中取,如果该属性为 null,那么就初始化。
当线程结束时,会调用当前线程实例的 exit() 方法,将 threadLocals 设置为 null,以便垃圾回收器将其回收掉。
// THread 类中的方法
private void exit() {
// ...
threadLocals = null;
// ...
}最后,有一点需要格外注意:用完 ThreadLocal 一定要记得手动调用 remove() 方法,否则可能会产生脏数据甚至产生内存泄漏。
为啥呢?上面不是说线程结束时,会将 threadLocals 置为 null 吗?
是的,线程结束时,确实会做清理工作。
但,如果线程一直不结束呢?如果线程会被复用呢?比如使用了线程池。
所以,使用 ThreadLocal 一定要手动 remove()。
- 完 -
边栏推荐
猜你喜欢
![验证二叉树的前序序列化[抽象前序遍历]](/img/14/461409ce34369db69e569215f91880.png)
验证二叉树的前序序列化[抽象前序遍历]

How to merge the code when there is a code conflict in the collaborative development of multiple people?

leetcode linked list topic

在金融服务行业数字化转型中,低代码值得被关注

【pytorch】1.6 tensor 基本运算

全开放式耳机怎么样?不塞耳朵的蓝牙耳机推荐

多人协作开发出现代码冲突,如何合并代码?

力扣 206.反转链表--递归解决

【论文阅读】异常检测的视频通过Self-Supervised和多任务学习

84.(cesium之家)cesium模型在地形上运动
随机推荐
【Postman】Download and installation (novice graphic tutorial)
九种方式,教你读取 resources 目录下的文件路径
函数柯里化
分布式事务方案
性能优化竟白屏,难道真是我的锅?
无线传感器网络定位综述
第4章_3——索引的使用
【堡垒机小知识】硬件堡垒机是什么意思?其与云堡垒机有什么区别?
【LeetCode】Day105-递增的三元子序列
带你了解一下PHP搭建的电商商城系统
【10点公开课】:快手GPU/FPGA/ASIC异构平台的应用探索
程序员入门的第一个程序,打印输出 “ HelloWorld “
城市污水处理过程模型预测控制研究综述
The Advanced Guide to the Computer Professional Interview
TAP 文章系列-10 | 从应用感知能力谈 TAP 的约定服务
Nine kinds of way, teach you to read the resources files in the directory path
R Error in :missing values are not allowed in subscripted assignments of data frames
84.(cesium之家)cesium模型在地形上运动
中国互联网科技企业群狼围攻,谷歌终于遭受重挫导致利润大跌,它为推动鸿蒙的发展而后悔...
FPGA刷题——跨时钟域传输(FIFO+打拍+握手)