彻底理解ThreadLocal的应用场景和底层实现

一.概念

定义:

ThreadLocal 是 Java 中所提供的线程本地存储机制,可以利用该机制将数据缓存在某个线程内部,该线程可以在任意时刻、任意方法中获取缓存的数据。

其实是可以通过调用 Set() 方法往里面存入值,存入的值是每个线程互相隔离、互不影响的,每个线程都有一个 ThreadLocal 的空间来存 ThreadLocal 的数据,那么它是怎么做到每个线程互相隔离、互不影响的呢?这就需要从底层数据结构分析了。

二.底层数据结构

先看下图:

1.在线程上,有一个叫ThreadLocalMap的变量,这个变量用来存储当前线程的ThreadLocal的数据,ThreadLocalMap里面可能有多个entry对象,每个entry都由一个Key-Value组成;

2.当调用threadLocal.set()方法的时候,其实就是先拿到当前线程中的ThreadLocalMap,拿到以后再将这个threadLocal放到entry的key上面,再将这个存储的值放在value里面;

3.在当前线程中使用了多个threadLocal时,会存储多个entry,并且这些entry都会绑定当前线程。

通过上诉的流程,就达到了每个线程的相互隔离、互不影响。

接下来再通过源码进行查看:

    ThreadLocal.ThreadLocalMap threadLocals = null;public void set(T value) {// 获取当前线程Thread t = Thread.currentThread();// 获取当前线程的ThreadLocalMap对象ThreadLocalMap map = getMap(t);// 不为空就直接插入当前ThreadLocalMap中if (map != null)map.set(this, value);elsecreateMap(t, value);}ThreadLocal.ThreadLocalMap getMap(Thread t) {return t.threadLocals;}

其实可以理解ThreadLocal其实是用来操作当前线程中的ThreadLocalMap的一个工具类,通过上诉的set()方法,就可以实现每个线程的相互隔离、互不影响。

总结:

ThreadLocal底层是通过ThreadLocalMap来实现的,每个Thread对象(注意不是ThreadLocal对象)中都存在一个ThreadLocalMap,Map的key为ThreadLocal对象,Map的value为需要缓存的值

三.使用注意事项

如果在线程池中使用ThreadLocal会造成内存泄漏,因为当ThreadLocal对象使用完之后,应该要把Entry对象(设置的key-value)进行回收,但线程池中的核心线程是不会被回收的,而线程对象是通过强引用指向ThreadLocalMap,ThreadLocalMap也是通过强引用指向Entry对象,线程不被回收,Entry对象也就不会被回收,从而出现内存泄漏,解决办法是,在使用了ThreadLocal对象之后,手动调用ThreadLocal的remove方法,手动清除Entry对象。

四.应用场景 

ThreadLocal的使用场景很多,但是它都遵守一个原则:当一个共享变量是共享的,但是需要每个线程互不影响,相互隔离,就可以使用ThreadLocal

日常开发常用的两种场景:

1.跨层传递信息的时候,每个方法都声明一个参数很麻烦, A\B\C\D 4个类互相传递每个方法都声明参数降低了维护性,可以用一个ThreadLocal共享变量,在A存值,BCD都可以获取;(比如有一个User需要在ABCD类中传递,那么4个类都需要定义User,并且当User的参数修改后,4个类都需要修改,这也不方便进行维护,所以可以用一个ThreadLocal共享变量来实现)

2.隔离线程,存储一些线程不安全的工具对象(如SimpleDateFormat,在多线程的场景下去做日期的格式化,就可能出现线程不安全的问题);

框架中的使用:

1.Spring中的事务管理器就是使用的ThreadLocal;(在多线程情况下,声明式事务是没办法达到一致性的,因为一个线程就存储了一个事务)

2.Springmvc的HttpSession、HttpServletReugest、HttpServletResponse都是放在ThreadLocal,因为servlet是单例的,而springmvc允许在controller类中通过@Autowired配置request、response以及requestcontext等实例对象,底层就是搭配Threadlocal才实现线程安全。(request、response如果是单列的,就会出现线程安全问题,因为每个请求都有自己的request和response,所以它们的底层都是通过ThreadLocal也进行存储的这些对象的)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/486738.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下redis环境的搭建

1.redis的下载 redis官网下载redis的linux压缩包,官网地址:Redis下载 网盘链接: 通过网盘分享的文件:redis-5.0.4.tar.gz 链接: https://pan.baidu.com/s/1cz3ifYrDcHWZXmT1fNzBrQ?pwdehgj 提取码: ehgj 2.redis安装与配置 将包上传到 /…

使用 pyperclip 进行跨平台剪贴板操作

简介:pyperclip 是一个轻量级的 Python 库,支持在不同操作系统(Windows、macOS、Linux)中进行剪贴板的复制和粘贴。这个库的设计简单易用,非常适合需要频繁进行文本复制粘贴操作的场景。 历史攻略: 使用f…

什么是初积分

在学习《高等动力学》时碰到一个概念“初积分”,为了方便记忆,在这里做个笔记。 1 定义 在常微分方程理论中,初积分是指对于一个给定的常微分方程组,如果存在一个可微函数,使得沿方程组的任何解,函数的值…

S32K324 HSE使用注意事项

文章目录 前言HSE安装完成后APP无法运行问题描述问题产生原因解决方案APP偶发获取不到HSE版本问题描述问题产生原因解决方案使能XRDC后,APP与HSE无法通信问题描述问题产生原因解决方案总结前言 在HSE使用过程中,出现过一些必现和偶发的问题,本文总结一下问题原因和解决方案…

fastadmin 登录退出忽略中间提示页面

背景 研究了一圈CMS,从fastadmin、easyadmin、buildadmin、onethink等等几乎所有的框架CMS,当然也包括若依。 最后,根据当前项目综合考虑,还是选择的fastadmin: 预算经济实惠、维护成本低;工期端&#x…

DApp开发前端框架选择:React还是Vue?

在区块链DApp开发中,前端框架的选择对用户体验和开发效率至关重要。React和Vue作为两大主流前端框架,各自拥有广泛的开发者基础和丰富的生态支持。那么在DApp开发中,该如何选择适合自己的框架呢?下面我们来比较一下,看…

证明网络中的流形成一个凸集

证明网络中的流形成一个凸集 步骤1:定义和符号步骤2:线性组合步骤3:验证容量限制步骤4:验证流量守恒结论示例代码(C语言) 在网络流理论中,一个流 f f f 是定义在网络图的边集上的一种函数&…

opencv复习

目录 1.core 1.图像变换 1.1 affine仿射变换 1.2 透视变换 2.四元数(旋转) 2.1 轴角转四元数 2.2 旋转矩阵转四元数 2.3 欧拉角转旋转矩阵 2.4 四元数转旋转矩阵 2.5 四元数用eigen用的比较多 2. imgproc. Image Processing 2.1 bilateralF…

分治_归并_归并排序(逆序对)

912. 排序数组 上一次我们做这道题时用的是数组划分三块的思想搭配随机选择基准元素的⽅法。 随机选择一个数,以这个数key为基准划分数组,小于key的数在左边,大于key的数在右边。再把被划分的两部份再找key值划分,直到只剩1或者0个…

环境兼容: Vue3+ELement-plus

题目:环境兼容: Vue3ELement-plus 前言 身为小白的我也在负责一个项目咯,开发的是Vue3项目,然后就搜阅多篇文章,整理了这个。内容很多是转载的,拼成的我这个文章。 Element-plus简介 Element-plus 是基于…

UE5基本数据类型

bool: 表示布尔值,只有两个取值:true 或 false,用于表示逻辑条件。int8: 表示 8 位的有符号整数,范围是 −128−128 到 127127。uint8: 表示 8 位的无符号整数,范围是 00 到 255255。int16: 表示 16 位的有符号整数&am…

【SpringMVC】参数传递 重定向与转发 REST风格

文章目录 参数传递重定向与转发REST风格 参数传递 ModelAndView:包含视图信息和模型数据信息 public ModelAndView index1(){// 返回页面ModelAndView modelAndView new ModelAndView("视图名");// 或// ModelAndView modelAndView new ModelAndView(…

软件工程 概述

软件 不仅仅是一个程序代码。程序是一个可执行的代码,它提供了一些计算的目的。 软件被认为是集合可执行的程序代码,相关库和文档的软件。当满足一个特定的要求,就被称为软件产品。 工程 是所有有关开发的产品,使用良好定义的&…

【数字化】华为企业数字化转型-认知篇

导读:企业数字化转型的必要性在于,它能够帮助企业适应数字化时代的需求,提升运营效率,创新业务模式,增强客户互动,从而在激烈的市场竞争中保持领先地位并实现可持续发展。通过学习华为企业数字化转型相关理…

Android学习15--charger

1 概述 最近正好在做关机充电这个,就详细看看吧。还是本着保密的原则,项目里的代码也不能直接用,这里就用的Github的。https://github.com/aosp-mirror 具体位置是:https://github.com/aosp-mirror/platform_system_core/tree/mai…

Leetcode刷题(81~90)

算法是码农的基本功,也是各个大厂必考察的重点,让我们一起坚持写题吧。 遇事不决,可问春风,春风不语,即是本心。 我们在我们能力范围内,做好我们该做的事,然后相信一切都事最好的安排就可以啦…

ARINC 标准全解析:航空电子领域多系列标准的核心内容、应用与重要意义

ARINC标准概述 ARINC标准是航空电子领域一系列重要的标准规范,由航空电子工程委员会(AEEC)编制,众多航空公司等参与支持。这些标准涵盖了从飞机设备安装、数据传输到航空电子设备功能等众多方面,确保航空电子系统的兼…

【数据库】关系代数和SQL语句

一 对于教学数据库的三个基本表 学生S(S#,SNAME,AGE,SEX) 学习SC(S#,C#,GRADE) 课程(C#,CNAME,TEACHER) (1)试用关系代数表达式和SQL语句表示:检索WANG同学不学的课程号 select C# from C where C# not in(select C# from SCwhere S# in…

学习记录:js算法(一百一十八):连接所有点的最小费用

文章目录 连接所有点的最小费用思路一 连接所有点的最小费用 给你一个points 数组,表示 2D 平面上的一些点,其中 points[i] [xi, yi] 。 连接点 [xi, yi] 和点 [xj, yj] 的费用为它们之间的 曼哈顿距离 :|xi - xj| |yi - yj| ,其…

Java项目实战II基于微信小程序的小区租拼车管理信息系统 (开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、核心代码 五、源码获取 全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着城市化进程的加速,小区居民对于出行方…