0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

ThreadLocal源码解析及实战应用

OSC开源社区 来源:OSCHINA 社区 2023-01-29 14:53 次阅读

来源| OSCHINA 社区

作者 | 京东云开发者-京东物流 闫鹏勃

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 中的使用

在登录拦截器中将用户信息写入,后续使用时方便取值

3e989f48-9693-11ed-bfe3-dac502259ad0.png3eb826a6-9693-11ed-bfe3-dac502259ad0.png

3.2 分页插件 PageHelper 中的应用

3eddc1a4-9693-11ed-bfe3-dac502259ad0.png3ef7e3cc-9693-11ed-bfe3-dac502259ad0.png

3.3 AopContext

3f0e2646-9693-11ed-bfe3-dac502259ad0.png

4 源码解读

你是否有这样的疑惑?为什么可以直接拿到?对象存放在哪里?存在什么问题?

4.1 get 方法

在 get () 方法中也会获取到当前线程的 ThreadLocalMap,如果 ThreadLocalMap 不为 null,则把获取 key 为当前 ThreadLocal 的值;否则调用 setInitialValue () 方法返回初始值,并保存到新创建的 ThreadLocalMap 中。 3f28a94e-9693-11ed-bfe3-dac502259ad0.png

4.2 set 方法

调用 set 时,直接调用 set (T value) 方法中,首先获取当前线程,然后在获取到当前线程的 ThreadLocalMap,如果 ThreadLocalMap 不为 null,则将 value 保存到 ThreadLocalMap 中,并用当前 ThreadLocal 作为 key;否则创建一个 ThreadLocalMap 并给到当前线程,然后保存 value。 ThreadLocalMap 相当于一个 HashMap,是真正保存值的地方
map 的 set,如果 map 为空,则创建一个

3f4305f0-9693-11ed-bfe3-dac502259ad0.png3f5e517a-9693-11ed-bfe3-dac502259ad0.png

4.3 initialValue () 方法

initialValue () 是 ThreadLocal 的初始值,默认返回 null,子类可以重写改方法,用于设置 ThreadLocal 的初始值。 3f7e11a4-9693-11ed-bfe3-dac502259ad0.png

4.4 remove () 方法

ThreadLocal 还有一个 remove () 方法,用来移除当前 ThreadLocal 对应的值。同样也是同过当前线程的 ThreadLocalMap 来移除相应的值。 3f9ef81a-9693-11ed-bfe3-dac502259ad0.png

getMap 拿到了什么?


在 set,get,initialValue 和 remove 方法中都会获取到当前线程,然后通过当前线程获取到 ThreadLocalMap,如果 ThreadLocalMap 为 null,则会创建一个 ThreadLocalMap,并给到当前线程 3fba01a0-9693-11ed-bfe3-dac502259ad0.png

此处 t 是 Thread,直接可以 “点” 拿到这个 map
每个 Thread 对象内部都维护了一个 ThreadLocalMap 这样一个 ThreadLocal 的 Map,可以存放若干个 ThreadLocal
3fcc3a32-9693-11ed-bfe3-dac502259ad0.png

在使用 ThreadLocal 类型变量进行相关操作时,都会通过当前线程获取到 ThreadLocalMap 来完成操作。每个线程的 ThreadLocalMap 是属于线程自己的,ThreadLocalMap 中维护的值也是属于线程自己的。这就保证了 ThreadLocal 类型的变量在每个线程中是独立的,在多线程环境下不会相互影响。

5 使用注意事项

1)有可能导致内存泄漏,使用完毕后,需要 remove 在 ThreadLocalMap 的 set (),get () 和 remove () 方法中,都有清除无效 Entry 的操作,这样做是为了降低内存泄漏发生的可能。


Entry 中的 key 使用了弱引用的方式,这样做是为了降低内存泄漏发生的概率,但不能完全避免内存泄漏。 3ffbe872-9693-11ed-bfe3-dac502259ad0.png


假设 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)使用线程池时,父子线程传递慎用,因为初始化时机为线程创建时

402e53b6-9693-11ed-bfe3-dac502259ad0.png

3)针对 2 有什么方案可以解决?
TransmittableThreadLocal
源码地址:https://github.com/alibaba/transmittable-thread-local

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据库
    +关注

    关注

    7

    文章

    3640

    浏览量

    63743
  • 源码
    +关注

    关注

    8

    文章

    599

    浏览量

    28734
  • spring
    +关注

    关注

    0

    文章

    334

    浏览量

    14208
  • 变量
    +关注

    关注

    0

    文章

    599

    浏览量

    28175
  • 线程
    +关注

    关注

    0

    文章

    497

    浏览量

    19545

原文标题:ThreadLocal 源码解析及实战应用

文章出处:【微信号:OSC开源社区,微信公众号:OSC开源社区】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    ThreadLocal实例应用

    ThreadLocal相信大家都用过,但你知道他的原理吗,今天了不起带大家学习ThreadLocalThreadLocal是什么 在多线程编程中,经常会遇到需要在不同线程中共享数据的情况
    的头像 发表于 09-30 10:19 516次阅读
    <b class='flag-5'>ThreadLocal</b>实例应用

    ThreadLocal的定义、用法及优点

    ThreadLocal 简介 ThreadLocal是Java中一个非常重要的线程技术。它可以让每个线程都拥有自己的变量副本,避免了线程间的竞争和数据泄露问题。在本文中,我们将详细介绍
    的头像 发表于 09-30 10:14 603次阅读
    <b class='flag-5'>ThreadLocal</b>的定义、用法及优点

    C语言实战105例源码

    C语言实战105例源码
    发表于 08-20 12:40

    Spark运行架构与源码解析

    Spark 源码解析DAGScheduler中的DAG划分与提交
    发表于 04-24 06:32

    用在解析云端数据的源码是怎样的

    用在解析云端数据的源码是怎样的?如何去实现这种源码呢?
    发表于 10-18 09:00

    uCOS3源码解析教程

    uCOS3源码解析视频教程-第4季第7部分 互联网课程品牌《朱老师物联网大讲...
    发表于 01-12 07:46

    对FreeRTOS的实战学习以及源码分析

    整个专栏主要是博主结合自身对FreeRTOS的实战学习以及源码分析,基于STM32F767 Nucleo-144平台,在CubeIDE下进行开发,结合官方的HAL库,将硬件环节的问题减少到最小,将精力主要放在RTOS的学习上
    发表于 02-11 07:18

    Uboot中start.S源码的指令级的详尽解析

    Uboot中start.S源码的指令级的详尽解析
    发表于 10-30 08:47 28次下载
    Uboot中start.S<b class='flag-5'>源码</b>的指令级的详尽<b class='flag-5'>解析</b>

    ThreadLocal发生内存泄漏的原因

    前言 ThreadLocal 的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。但是如果滥用 ThreadLocal
    的头像 发表于 05-05 16:23 3536次阅读

    如何使用ThreadLocal来避免内存泄漏

    本次给大家介绍重要的工具ThreadLocal。讲解内容如下,同时介绍什么场景下发生内存泄漏,如何复现内存泄漏,如何正确使用它来避免内存泄漏。 ThreadLocal是什么?有哪些用途
    的头像 发表于 08-20 09:29 3966次阅读
    如何使用<b class='flag-5'>ThreadLocal</b>来避免内存泄漏

    简述hex文件解析源码

    简述hex文件解析源码
    发表于 09-12 09:20 8次下载

    HarmonyOS测试技术与实战-HarmonyOS图形栈测试技术深度解析

    HDC 2021华为开发者大会HarmonyOS测试技术与实战-HarmonyOS图形栈测试技术深度解析
    的头像 发表于 10-23 15:09 1347次阅读
    HarmonyOS测试技术与<b class='flag-5'>实战</b>-HarmonyOS图形栈测试技术深度<b class='flag-5'>解析</b>

    云海计费系统v4.1 视频解析解析收费接口专用 短视频解析解析收费接口专用 影视视频电影解析计费平台源码程序

    介绍:云海计费系统v4.1 视频解析 短视频解析 影视视频电影解析计费平台源码程序云海解析计费系统是一款VIP视频计费
    发表于 01-11 16:02 13次下载
    云海计费系统v4.1 视频<b class='flag-5'>解析</b><b class='flag-5'>解析</b>收费接口专用 短视频<b class='flag-5'>解析</b><b class='flag-5'>解析</b>收费接口专用 影视视频电影<b class='flag-5'>解析</b>计费平台<b class='flag-5'>源码</b>程序

    node.js实战源码

    node.js实战源码
    发表于 05-16 18:06 0次下载

    ThreadLocal基本内容与用法

    下面我们就来看看道哥都用的ThreadLocal。 1 ThreadLocal你来自哪里 Since : 1.2 Author : Josh Bloch and Doug Lea 又是并发大佬
    的头像 发表于 10-13 11:39 321次阅读