ThreadLocal源码解析及实战应用
作者:京东物流 闫鹏勃
1 什么是ThreadLocal?
ThreadLocal是一个关于创建线程局部变量的类。
通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用ThreadLocal创建的变量只能被当前线程访问,其他线程则无法访问和修改。ThreadLocal在设计之初就是为解决并发问题而提供一种方案,每个线程维护一份自己的数据,达到线程隔离的效果。
2 有什么作用?
2.1 set once,get everywhere
在现在的系统设计中,前后端分离已基本成为常态,分离之后如何获取用户信息就成了一件麻烦事,通常在用户登录后, 用户信息会保存在Session或者Token中。这个时候,我们如果使用常规的手段去获取用户信息会很费劲,拿Session来说,我们要在接口参数中加上HttpServletRequest对象,然后调用 getSession方法,且每一个需要用户信息的接口都要加上这个参数,才能获取Session,这样实现就很麻烦了。
在实际的系统设计中,我们肯定不会采用上面所说的这种方式,而是使用ThreadLocal,我们会选择在拦截器的业务中, 获取到保存的用户信息,然后存入ThreadLocal,那么当前线程在任何地方如果需要拿到用户信息都可以使用ThreadLocal的get()方法 (异步程序中ThreadLocal是不可靠的)
2.2 线程安全,空间换时间
在Spring的Web项目中,我们通常会将业务分为Controller层,Service层,Dao层, 我们都知道@Autowired注解默认使用单例模式,那么不同请求线程进来之后,由于Dao层使用单例,那么负责数据库连接的Connection也只有一个, 如果每个请求线程都去连接数据库,那么就会造成线程不安全的问题,Spring是如何解决这个问题的呢?
在Spring项目中Dao层中装配的Connection肯定是线程安全的,其解决方案就是采用ThreadLocal方法,当每个请求线程使用Connection的时候, 都会从ThreadLocal获取一次,如果为null,说明没有进行过数据库连接,连接后存入ThreadLocal中,如此一来,每一个请求线程都保存有一份 自己的Connection。于是便解决了线程安全问题
3 ThreadLocal实战应用
3.1 ehr中的使用
在登录拦截器中将用户信息写入,后续使用时方便取值
3.2 分页插件PageHelper中的应用
3.3 AopContext
4 源码解读
你是否有这样的疑惑?为什么可以直接拿到?对象存放在哪里?存在什么问题?
4.1 get方法
在 get() 方法中也会获取到当前线程的 ThreadLocalMap,如果 ThreadLocalMap 不为 null,则把获取 key 为当前 ThreadLocal 的值;否则调用 setInitialValue() 方法返回初始值,并保存到新创建的 ThreadLocalMap 中。
4.2 set方法
调用set时,直接调用set(T value) 方法中,首先获取当前线程,然后在获取到当前线程的 ThreadLocalMap,如果 ThreadLocalMap 不为 null,则将 value 保存到 ThreadLocalMap 中,并用当前 ThreadLocal 作为 key;否则创建一个 ThreadLocalMap 并给到当前线程,然后保存 value。
ThreadLocalMap 相当于一个 HashMap,是真正保存值的地方
map的set,如果map为空,则创建一个
4.3 initialValue() 方法
initialValue() 是 ThreadLocal 的初始值,默认返回 null,子类可以重写改方法,用于设置 ThreadLocal 的初始值。
4.4 remove() 方法
ThreadLocal 还有一个 remove() 方法,用来移除当前 ThreadLocal 对应的值。同样也是同过当前线程的 ThreadLocalMap 来移除相应的值。
getMap拿到了什么?
在 set,get,initialValue 和 remove 方法中都会获取到当前线程,然后通过当前线程获取到 ThreadLocalMap,如果 ThreadLocalMap 为 null,则会创建一个 ThreadLocalMap,并给到当前线程
此处t是Thread,直接可以“点”拿到这个map
每个Thread对象内部都维护了一个ThreadLocalMap这样一个ThreadLocal的Map,可以存放若干个ThreadLocal
在使用 ThreadLocal 类型变量进行相关操作时,都会通过当前线程获取到 ThreadLocalMap 来完成操作。每个线程的 ThreadLocalMap 是属于线程自己的,ThreadLocalMap 中维护的值也是属于线程自己的。这就保证了 ThreadLocal 类型的变量在每个线程中是独立的,在多线程环境下不会相互影响。
5 使用注意事项
1)有可能导致内存泄漏,使用完毕后,需要remove
在 ThreadLocalMap 的 set(),get() 和 remove() 方法中,都有清除无效 Entry 的操作,这样做是为了降低内存泄漏发生的可能。
Entry 中的 key 使用了弱引用的方式,这样做是为了降低内存泄漏发生的概率,但不能完全避免内存泄漏。
假设 Entry 的 key 没有使用弱引用的方式,而是使用了强引用:由于 ThreadLocalMap 的生命周期和当前线程一样长,那么当引用 ThreadLocal 的对象被回收后,由于 ThreadLocalMap 还持有 ThreadLocal 和对应 value 的强引用,ThreadLocal 和对应的 value 是不会被回收的,这就导致了内存泄漏。所以 Entry 以弱引用的方式避免了 ThreadLocal 没有被回收而导致的内存泄漏,但是此时 value 仍然是无法回收的,依然会导致内存泄漏。
ThreadLocalMap 已经考虑到这种情况,并且有一些防护措施:在调用 ThreadLocal 的 get(),set() 和 remove() 的时候都会清除当前线程 ThreadLocalMap 中所有 key 为 null 的 value。这样可以降低内存泄漏发生的概率。所以我们在使用 ThreadLocal 的时候,每次用完 ThreadLocal 都调用 remove() 方法,清除数据,防止内存泄漏。
2)使用线程池时,父子线程传递慎用,因为初始化时机为线程创建时
3)针对2有什么方案可以解决?
TransmittableThreadLocal
源码地址: https://github.com/alibaba/transmittable-thread-local
详解:https://www.jianshu.com/p/e0774f965aa3

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
LibcarePlus 用户态热补丁技术那些事
LibcarePlus 用户态热补丁作为 openEuler 社区关键技术,受到了广大开发者的关注和讨论。openEuler 社区与天翼云基础架构技术团队通力合作,共同打造了全面支持 aarch64 及 x86 平台的 LibcarePlus 热补丁功能,可以应用于 CVE 漏洞修复,也可应用于不中断应用服务的紧急 bug 修复。下面我们就来聊聊什么是热补丁技术。 热补丁技术背景 当运行的程序存在漏洞的时候,我们一般有以下几种解决办法: 替换最新的包含修复补丁的 OS 版本,在有主备倒换能力的组网环境上,可以先将当前运行的程序迁移到备区,待主区升级完后,再将程序迁移到主区; 替换程序的 rpm 包,然后重新运行程序(注意此时需要考虑依赖包的兼容性),对于具备热替换能力的程序,则可以利用热替换能力,实现程序运行内容的替换; 直接给运行的程序内容打补丁,替换有问题的代码,实现程序漏洞的修复; 从补丁粒度上来说,上述技术漏洞修复粒度是从大到小变化的:第一个是系统级的,主要涉及的技术有热迁移技术;第二个是 rpm 包级的,主要涉及的技术有热替换;第三个是运行程序级的,主要涉及的技术有热补丁。从...
- 下一篇
【DTCC 2022】云原生数据库PieCloudDB全新eMPP架构是如何炼成的
12月14-16日,第十三届中国数据库技术大会(DTCC 2022)在线上隆重召开。拓数派赞助并参与了数据库盛会DTCC,在会议中,拓数派CTO 郭罡分享了《云原生数据库 PieCloudDB eMPP架构设计与实现》的主题演讲。在演讲中,郭罡分析了传统分布式MPP架构的痛点,介绍了云原生数据库PieCloudDB的eMPP架构的设计背景与重要功能组件。本文整理自现场演讲内容。 相关PPT欢迎访问拓数派官网链接进行预览和下载。演讲视频欢迎访问B站链接。 传统分布式MPP架构痛点 MPP(Massive Parallel Processing,大规模并行处理),一直被誉为当今数据库的主流架构,被广泛用于众多数据库产品中,包括Greenplum、Teradata、Vertica等。MPP分析型数据库针对分析工作负载进行了大量优化,以满足用户聚合和处理大型数据集的需求。它会将任务并行的分布到多个服务器和节点上,并在完成计算后,将结果返回并汇总,从而完成对海量数据的分析处理。 然而,传统分布式MPP架构存在很多瓶颈,我们将客户的真实生产场景中遇到的问题进行归纳,总结出5个痛点: 缺乏弹性 传...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Red5直播服务器,属于Java语言的直播服务器
- CentOS7设置SWAP分区,小内存服务器的救世主
- CentOS7安装Docker,走上虚拟化容器引擎之路
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- CentOS关闭SELinux安全模块
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16