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

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

时间2025-08-05 06:04:40分类IT科技浏览4318
导读:作者:京东物流 闫鹏勃 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
wordpress建网站详细教程(WordPress建站:打造个性化网站从此不再困难)