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

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

时间2025-09-19 17:46:16分类IT科技浏览4956
导读:作者:京东物流 闫鹏勃 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
你看的手机是什么样的(你也该考虑看看手机的外形是不是配合你的气质而不是只盯着【使用体验】了。)