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

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

时间2025-05-05 16:28:53分类IT科技浏览3161
导读:作者:京东物流 闫鹏勃 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
伪原创写文章软件(让你的文章瞬间升级! 伪原创在线编辑教你如何轻松做到)