首页IT科技threadlocal源码分析(ThreadLocal源码解析及实战应用)

threadlocal源码分析(ThreadLocal源码解析及实战应用)

时间2025-06-14 16:56:42分类IT科技浏览3710
导读:作者:京东物流 闫鹏勃 1 什么是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

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
opencv人脸检测代码(【Android App】人脸识别中借助摄像头和OpenCV实时检测人脸讲解及实战(附源码和演示 超详细)) 芳村seo优化的攻略(芳村seo技巧方法方式)