为什么一个线程中有两个ThreadLocalMap?

本来是想介绍Scoped Values的,但是牵扯到ThreadLocalInheritableThreadLocal,所以先来介绍这两个技术,同时解决一个问题:为什么一个线程中有两个ThreadLocalMap?

先贴出Thread类中的源码方便理解问题:

先给出结论:当我们使用ThreadLocal来共享值时用的是线程中的threadLocals属性,当我们使用InheritableThreadLocal来共享值时用的是inheritableThreadLocals属性。

先聊聊ThreadLocal

一个线程 串行执行多个方法时,如果需要在这多个方法中共享变量时,就可以通过ThreadLocal来进行传递。但并不是每个线程都需要一个ThreadLocal对象,而是多个线程可以共用一个ThreadLocal对象,比如看下面代码:

变量NAME就是一个ThreadLocal对象,如果此时有线程1、线程2同时来调用方法a(),并传入不同的参数,那么这两个线程使用的就是同一个ThreadLocal对象,在a()方法中给NAME设置不同的值,然后这两个线程在执行b()、c()方法时,从NAME中也将拿到不同的值。

思考:ThreadLocal对象的set()和get()方法是如何区分不同线程保存所设置的值的呢?

答案呼之欲出,通过获取到执行set()和get()方法的当前线程 自然就能区分不同线程,然后使用Map 来保存所设置的值就可以了,所以set()的源码如下,其中就获取了当前线程:

但是上面这段代码有误导性,会让人觉得当前线程就是Map的key,但是我们继续看内层set()方法的源码:

源码中,getMap(t)方法会获取当前线程对象的threadLocals属性,从而得到一个ThreadLocalMap对象,然后把当前正在执行set()方法的ThreadLocal对象(源码中的this)作为key,set的值作为value,存入ThreadLocalMap,所以Map的key是ThreadLocal对象。

而对于ThreadLocal的get()方法就比较容易理解了,直接看源码:

get()方法的核心流程:先拿到当前线程对象,从线程中获取ThreadLocalMap,最后将当前在调用get()方法的ThreadLocal对象作为key从ThreadLocalMap中获取value返回。

对于ThreadLocal再做一次总结:每个线程中有一个 threadLocals属性对应的ThreadLocalMap,线程在执行ThreadLocal对象的set()、get()方法时,会把ThreadLocal对象作为key并利用这个ThreadLocalMap设置值或获取值

再聊聊InheritableThreadLocal

线程中另外一个属性inheritableThreadLocals也是一个ThreadLocalMap,InheritableThreadLocal类继承了ThreadLocal类,但是InheritableThreadLocal的真正意义是:子线程会继承父线程中的 inheritableThreadLocals对应的ThreadLocalMap,而且是复制一份,也就是子线程中的ThreadLocalMap和父线程中的ThreadLocalMap中的内容一开始是一样的,但是后续的修改将互不影响,注意当我们用InheritableThreadLocal时,线程中的 threadLocals属性就没有作用了。

一段代码就可以看出效果: 当使用ThreadLocal时,以上代码中子线程是获取不到值的,但是如果改成用InheritableThreadLocal则能获取到值。

如果在子线程中修改NAME的值也不会影响父线程:

当调用InheritableThreadLocal的set()方法时,最终会执行到InheritableThreadLocal的getMap()方法: 所以InheritableThreadLocal用的是线程对象中的inheritableThreadLocals属性。

而在new Thread()时,在构造方法里会把父线程中的inheritableThreadLocals的ThreadLocalMap内容复制 给当前子线程的inheritableThreadLocals:

好了,以上就是关于ThreadLocal与InheritableThreadLocal的介绍与分析了,希望大家有收获,那么问题来了:ThreadLocal有哪些缺点呢?为什么JDK21的新特性之一 Scoped Values跟ThreadLocal、InheritableThreadLocal又有关系呢?

关注我,我们下期见。

广告

我准备运营自己的知识星球啦,知识星球以免费答疑、优化简历、讨论技术 为主,另外还能帮助大家提升工作效率快速拿到offer认识更多大牛学习更多技术。

前50名 小伙伴(发文时只有最后10个了),可以免费 加入星球,私信我领取免费链接!

如果您愿意直接付费 加入,那当然也是可以的啦

相关推荐
猪猪拆迁队36 分钟前
虚拟工厂仿真引擎的架构设计:让一条产线可编程、可观测、可干预
后端·ai编程
字节跳动数据库1 小时前
文章分享——相似函数处理方法
人工智能·后端·程序员
云技纵横1 小时前
@Transactional 失效的 7 种场景:第 5 种最难排查
后端
用户6757049885021 小时前
你知道 Go 结构体和结构体指针调用的区别吗?一文带你彻底搞懂!
后端·go
程序员cxuan2 小时前
读懂 Claude Code 架构分析系列,第一篇,开始!
人工智能·后端·架构
用户6757049885022 小时前
面试官问“装饰器模式”,这样回答薪资多要 3000!
后端
tntxia2 小时前
Geo Scene域名修改引起的一些问题
后端
用户298698530142 小时前
Java 实现 Word 文档加密与权限解除
java·后端
vanuan2 小时前
给你的A2A-Agent加把锁-认证鉴权实战指南
后端
Yeats_Liao2 小时前
14:Servlet中的页面跳转-Java Web
java·后端·架构