当前位置:网站首页>为什么 ThreadLocal 可以做到线程隔离?
为什么 ThreadLocal 可以做到线程隔离?
2022-07-26 19:37: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-1
OK,从效果上看,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()。
更多独家精彩内容尽在我的新书《Spring Boot趣味实战课》中。
边栏推荐
- URL格式
- numpy.newaxis
- Typescript asynchronous function promise use
- 【Pytorch进阶】pytorch模型的保存与使用
- .NET GC工作流程
- Kingbasees SQL language reference manual of Jincang database (19. SQL statement: drop table to load)
- 本机号码一键登录原理与应用(荣耀典藏版)
- SQL优化的一些建议,希望可以帮到和我一样被SQL折磨的你
- 2000 words to help you master anti shake and throttling
- EasyCVR设备管理列表页面,分页数据不显示的问题修复
猜你喜欢
随机推荐
Exchange 2010 SSL证书安装文档
数字化工厂的优势有哪些
2022 极术通讯-安谋科技开启商业化新篇章
有点酷,使用 .NET MAUI 探索太空
three. JS tag and pop-up the earth
Silent desktop fan chip dltap703sd Jericho
Use request header authentication to test API interfaces that need authorization
vs如何读取mysql中的数据(顺便通过代码解决了中文乱码问题)
How can small companies break through with small and beautiful products?
【Pytorch进阶】pytorch模型的保存与使用
Week 6 Convolutional Neural Networks (CNNs)
STM32F103有源蜂鸣器驱动程序
Kingbasees SQL language reference manual of Jincang database (19. SQL statement: drop table to load)
Jincang database kingbasees SQL language reference manual (18. SQL statement: drop materialized view to drop synonym)
Small scenes bring great improvement! Baidu PaddlePaddle easydl helps AI upgrade of manufacturing assembly line
Intranet penetration learning (II) information collection
贴合课标新方向 猿辅导打造特色新概念内容体系
C # convert PDF files into pictures
Typescrip异步函数Promise使用
谷歌的新编程语言被称为 Carbon








