当前位置:网站首页>为什么 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趣味实战课》中。
边栏推荐
- gospel! Wechat personal official account can be renamed!
- 本机号码一键登录原理与应用(荣耀典藏版)
- Use request header authentication to test API interfaces that need authorization
- Impersonate authentication
- ES6 method & Class array into real array & method of judging array
- Fasttunnel open source intranet penetration framework
- 第二章:遇到阻难!绕过WAF过滤!【SQL注入攻击】
- 有点酷,使用 .NET MAUI 探索太空
- 7岁男童因下棋太快?机器人竟夹断其手指
- Jincang database kingbasees SQL language reference manual (18. SQL statement: drop materialized view to drop synonym)
猜你喜欢
随机推荐
贴合课标新方向 猿辅导打造特色新概念内容体系
Typescript asynchronous function promise use
Strengthen supervision on secret room escape and script killing, and focus on strengthening fire safety and juvenile protection
任务一 报告
并行执行(二)、multiprocessing
three. JS tag and pop-up the earth
开源闹出乌龙事件,可能你不知道这五种开源协议
猿辅导的科技硬实力:让AI从读懂孩子作业开始
I tried many report tools and finally found a report based on Net 6
解决AttributeError: module ‘win32com.gen_py.00020813-0000-0000-C000-000000000046x0x1x9‘ has no attribu
Fasttunnel open source intranet penetration framework
Meeting seating & submission of meeting OA
打字比赛圆满结束!
MySQL InnoDB engine (V)
Kingbasees SQL language reference manual of Jincang database (17. SQL statement: discard to drop language)
EtherCAT 同步模式
MySQL之InnoDB引擎(五)
numpy.put()
SQL优化的一些建议,希望可以帮到和我一样被SQL折磨的你
福音!微信个人公众号可以改名了!









