ThreadLocal的使用介绍和底层原理解析和开源框架的使用实例
文章目录
- ThreadLocal的使用介绍和底层原理解析和开源框架的使用实例
- ThreadLocal简介
- ThreadLocal使用示例
- ThreadLocal原理解析
- Spring中ThreadLocal的应用
- 小结
- ThreadLocal的使用步骤
- 常见面试题
- 案例解析(框架源码经典案例)
- 案例实战
ThreadLocal的使用介绍和底层原理解析和开源框架的使用实例
ThreadLocal简介
ThreadLocal是一个线程内部的数据存储类,它可以为每个线程提供独立的变量副本,不同线程间的变量无法相互访问和修改。这避免了每个线程都要维护一套独立变量的麻烦,并且也减少了线程之间不必要的数据争用。ThreadLocal适用于这样的场景:每个线程需要有自己单独的实例,而不是共享实例。例如,在 web 应用中,每个请求被一个新的线程处理,每个线程需要有自己的变量实例。
ThreadLocal使用示例
public class ThreadLocalExample {// 线程局部变量,每个线程有自己的变量副本private ThreadLocal<String> threadLocal = new ThreadLocal<>();public void set(String value) {threadLocal.set(value);}public String get() {return threadLocal.get();}
}public class ThreadLocalTest {public static void main(String[] args) {ThreadLocalExample example = new ThreadLocalExample();// 线程1设置threadLocal变量example.set("Thread1 local variable"); System.out.println("Thread1 get: " + example.get());// 线程2无法获取线程1设置的threadLocal变量Thread thread2 = new Thread() {public void run() {example.set("Thread2 local variable"); System.out.println("Thread2 get: " + example.get());}};thread2.start();}
}
运行结果:
Thread1 get: Thread1 local variable
Thread2 get: Thread2 local variable每个线程获取自己设置的值,并不同线程间互不干扰。
ThreadLocal原理解析
ThreadLocal内部使用ThreadLocalMap来存储每个线程的变量副本。ThreadLocalMap是ThreadLocal的静态内部类,每个线程都有自己的ThreadLocalMap副本。
ThreadLocal中get()方法的实现如下:
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();
}
- 获取当前线程对象
- 获取当前线程的ThreadLocalMap(实际上是从当前线程的ThreadLocalMap变量中获取)
- 在ThreadLocalMap中获取当前ThreadLocal变量对应的value值
- 如果不存在,调用setInitialValue()方法初始化value值,并存储到ThreadLocalMap中
这样,每个线程的ThreadLocal变量都被存储在自己的ThreadLocalMap中,相互独立,互不干扰。
ThreadLocalMap使用ThreadLocal对象作为key来存储value值。当ThreadLocal对象被回收时,由弱引用产生的key会在下一次GC时被清除,这会导致value值无法被访问到,出现内存泄漏,所以我们应该手动调用remove()方法,在ThreadLocal不再使用时清除它。
Spring中ThreadLocal的应用
Spring框架中大量使用了ThreadLocal,例如:
- TransactionSynchronizationManager: 管理线程事务上下文信息。
- RequestContextHolder: 存储request上下文,用于获取request信息。
- LocaleContextHolder: 存储locale上下文,用于获取locale信息。
这些类都使用ThreadLocal来为每个线程提供单独变量副本,避免了线程间数据交叉和覆盖的问题。
@Component
public class RequestHolder {private ThreadLocal<HttpServletRequest> requestHolder = new ThreadLocal<>();public void setRequest(HttpServletRequest request) {requestHolder.set(request);}public HttpServletRequest getRequest() {return requestHolder.get(); }
}
这样每个线程在处理request时可以调用setRequest()方法存储自己的request对象,在其他地方需要获取request信息时,调用getRequest()方法即可获取当前线程对应的request对象。这就避免了每个线程都要维护一个request对象的麻烦,也减少了线程之间request对象混淆的问题。
小结
ThreadLocal为每个线程提供独立的变量副本,实现了线程隔离。它的主要作用是为每个线程保存一些 thread-local 的上下文信息,这些信息在线程的生命周期内起作用。
它的内部原理是使用ThreadLocalMap来存储每个线程对应的变量副本,键值为ThreadLocal对象,值则为变量副本。
它应用在许多地方,如Spring框架等,用于避免线程间数据交叉和覆盖的问题。
但是它也有一定的弊端,由于ThreadLocalMap使用ThreadLocal作为key,如果ThreadLocal被回收,就可能出现内存泄漏的问题。所以应该手动调用ThreadLocal的remove()方法,在ThreadLocal不再使用时清除它。
ThreadLocal的使用步骤
- 定义ThreadLocal变量:
private ThreadLocal<String> threadLocal = new ThreadLocal<>();
- 在每个线程内设置ThreadLocal变量:
threadLocal.set("value");
- 获取ThreadLocal变量:
String value = threadLocal.get();
- 删除ThreadLocal变量:
threadLocal.remove();
- 目前ThreadLocal类提供的方法有:
- set(T value): 设置当前线程的thread local变量的值。
- get(): 获取当前线程的thread local变量的值。
- remove(): 删除当前线程的thread local变量的值。
- initialValue(): 返回当前线程第一次调用get()时的值,后续调用get()会直接返回这个值。
- ThreadLocal应用举例:
- 解决数据库连接共享问题:每个线程都有自己的数据库连接,避免线程之间的连接混用。
private ThreadLocal<Connection> connectionHolder = new ThreadLocal<>();public Connection getConnection() {Connection conn = connectionHolder.get();if (conn == null) {conn = DriverManager.getConnection("jdbc:mysql://localhost/test", "root", "root");connectionHolder.set(conn);}return conn;
}
- 解决Session共享问题:每个线程都有自己的Session实例。
private ThreadLocal<HttpSession> sessionHolder = new ThreadLocal<>();public HttpSession getSession() {HttpSession session = sessionHolder.get();if (session == null) {session = request.getSession(); sessionHolder.set(session);}return session;
}
- ThreadLocal内存泄漏问题的解决:
由于ThreadLocalMap使用ThreadLocal作为key来存储entry,如果ThreadLocal被回收,key变成null,就会出现内存泄漏。所以ThreadLocal使用完毕后,需要调用remove()方法清除数据,避免出现内存泄漏。
threadLocal.remove();
常见面试题
- ThreadLocal能否解决线程安全问题?
答:ThreadLocal不能解决线程安全问题。ThreadLocal为每个线程提供独立的变量副本,实现线程隔离,但并不保证线程安全。如果多个线程同时修改同一个ThreadLocal变量,还是需要额外的同步措施保证线程安全。
- ThreadLocal会引起内存泄漏么?如何避免?
答:ThreadLocal会引起内存泄漏。因为ThreadLocalMap使用ThreadLocal作为key来存储entry,如果ThreadLocal被回收,key变成null,就会出现内存泄漏。
解决方法是在ThreadLocal不再使用时,手动调用remove()方法清除数据,避免出现内存泄漏。
- ThreadLocal的value为什么推荐使用引用类型?
答:因为每个线程访问自己的副本变量,如果使用基本类型,ThreadLocal需要为每个线程创建一个变量副本,这会消耗较多内存。
而如果使用引用类型,每个线程访问的都是同一个引用对象的副本,只是每个线程可以对这个对象进行修改,这可以节省内存,所以推荐ThreadLocal的value使用引用类型。
- ThreadLocalMap的工作原理是什么?
答:ThreadLocalMap是ThreadLocal的静态内部类,每个线程都有自己的ThreadLocalMap副本。
ThreadLocalMap使用ThreadLocal对象作为key来存储value值。在调用ThreadLocal的get()方法时,会先得到当前线程的ThreadLocalMap,然后再从其中获取与当前ThreadLocal对象关联的值。
put方法会将ThreadLocal对象作为key放入map中,并关联一个value。
当ThreadLocal对象被回收时,由弱引用产生的key会在下一次GC时被清除,这会导致value值无法被访问到,出现内存泄漏,所以在ThreadLocal不再使用时需要手动调用remove()方法清除数据。
- 个人理解ThreadLocal的主要作用和应用场景?
答:ThreadLocal的主要作用是为每个线程提供独立的变量副本,实现线程隔离。
它的应用场景主要有:
- 为每个线程绑定请求相关数据,避免同一个请求被不同线程处理时出现数据混淆的问题。
- 为每个线程单独绑定数据库连接、Session等资源,避免线程间共享资源。
- 解决变量共享导致的线程安全问题,通过给每个线程独立变量副本来隔离线程。
案例解析(框架源码经典案例)
这里我们以Spring中的ThreadLocal应用举个例子加深理解。
Spring中TransactionSynchronizationManager使用ThreadLocal来管理事务上下文信息。它定义了两个ThreadLocal变量:
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<Map<Object, Object>>("Transactional resources");
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<Set<TransactionSynchronization>>("Transaction synchronizations");
- resources: 用于存储事务相关资源,如数据库连接、Session等。
- synchronizations: 用于存储事务同步对象,如事务完成后需要执行的回调等。
当开始一个事务时,通过TransactionSynchronizationManager进行事务上下文的存储:
StaticTransactionSynchronizationAdapter.registerSynchronization(new TransactionSynchronizationAdapter() {@Overridepublic void afterCompletion(int status) {if (status == STATUS_COMMITTED) {afterCommit();}else {afterRollback(); }}}
);
TransactionSynchronizationManager.bindResource(ds, conn);
- 向synchronizations ThreadLocal集合中添加一个事务同步对象,用于在事务完成后执行回调。
- 向resources ThreadLocal集合中添加连接资源,以方便事务管理器管理。
事务完成时,TransactionSynchronizationManager会执行:
TransactionSynchronizationManager.initSynchronization();
try {// 执行回调TransactionSynchronizationManager.cleanupSynchronization();
}
finally {// 清除ThreadLocal上的变量TransactionSynchronizationManager.clear();
}
- 调用initSynchronization执行注册的同步回调。
- 调用cleanupSynchronization执行后续清理工作。
- 调用clear()方法清除ThreadLocal上的事务上下文,避免内存泄漏。
这样,通过ThreadLocal为每个事务线程独立存储事务上下文,避免了线程间数据混淆和干扰的问题。同时也在事务完成后手动调用clear()方法清除ThreadLocal,解决了内存泄漏的问题。
这就是ThreadLocal在Spring事务管理中的典型应用, hope这能加深您对ThreadLocal用法的理解。
案例实战
这里我们来实现一个简单的Session管理,使用ThreadLocal为每个线程单独绑定Session实例。
public class SessionManager {private ThreadLocal<HttpSession> sessionHolder = new ThreadLocal<>();public HttpSession getSession() {HttpSession session = sessionHolder.get();if (session == null) {session = request.getSession(); sessionHolder.set(session);}return session;}public void clear() {sessionHolder.remove();}
}
- 定义ThreadLocal变量sessionHolder来存储每个线程的HttpSession实例。
- getSession()方法先从sessionHolder中获取 SESSION,如果不存在则创建一个新的SESSION,并存储到sessionHolder中。
- clear()方法用于手动清除sessionHolder,避免内存泄漏。
使用方式:
// 获取Session
SessionManager manager = new SessionManager();
HttpSession session = manager.getSession();// 使用Session
session.setAttribute("key", "value");// 获取Attribute
String value = (String) session.getAttribute("key");// 手动清除
manager.clear();
使用ThreadLocal为每个线程单独存储SESSION,避免了线程间SESSION实例的混淆,也能很好地管理SESSION生命周期。
同时也演示了如何防止ThreadLocal内存泄漏的问题,手动调用clear()方法清除ThreadLocal变量。此案例结合理论介绍了ThreadLocal的整个使用过程,包括定义ThreadLocal变量,为每个线程单独设置变量值,获取变量值,清除ThreadLocal变量等步骤。并分析了其工作原理和应用场景,希望能够帮助大家进一步理解和熟练掌握ThreadLocal。
相关文章:
ThreadLocal的使用介绍和底层原理解析和开源框架的使用实例
文章目录 ThreadLocal的使用介绍和底层原理解析和开源框架的使用实例ThreadLocal简介ThreadLocal使用示例ThreadLocal原理解析Spring中ThreadLocal的应用小结ThreadLocal的使用步骤常见面试题案例解析(框架源码经典案例)案例实战 ThreadLocal的使用介绍和底层原理解析和开源框架…...
带你学c带你飞-P7取值范围
比特位 CPU能读懂的最小单元——比特位,bit,b 字节 内存机构的最小寻址单元——字节,Byte,B 1Byte8bit 进制 怎么算 注意:int默认是signed类型,signed类型第一位是符号位 符号位 存放signed类型的存…...
ramfs, rootfsinitramfs
什么是ramfs? ramfs是一个非常简单的文件系统,它将Linux的磁盘缓存机制(页面缓存和dentry缓存)导出为一个动态可调整大小的基于ram的文件系统。 Linux通常将所有文件缓存在内存中。从后备存储(通常是挂载文件系统的块设备)读取的数据页被保留下来,以防…...
十三届蓝桥杯研究生组国赛-最大公约数(线段树+二分)
十三届蓝桥杯研究生组国赛-最大公约数 1、问题描述2、解题思路2.1 解法一:暴力查询区间gcd(75%)2.2 解法二:线段树+二分法(AC)1、问题描述 问题描述 给定一个数组, 每次操作可以选择数组中任意两个相邻的元素 x , y x,y x,y...
数据结构——二叉树层序遍历
数据结构——二叉树层序遍历 107. 二叉树的层序遍历 II199. 二叉树的右视图思路: 637. 二叉树的层平均值 107. 二叉树的层序遍历 II 107. 二叉树的层序遍历 II 给你二叉树的根节点 root ,返回其节点值 自底向上的层序遍历 。 (即按从叶子节…...
【微机原理】8088/8086微处理器
目录 一、8088/8086的功能结构 1.总线接口部件(BIU) 2.执行部件(EU) 二、8088/8086的寄存器结构(14个) 溢出标志的概念 溢出和进位的区别 8086CPU是Intel系列的16位微处理器,他有16根数据…...
springboot第12集:DAO功能代码
在Spring Boot中,DAO是数据访问对象的缩写,它是一种设计模式用于提供对数据库操作的抽象层。通过使用DAO模式,我们可以将数据操作与业务逻辑分离,并提供一个单独的接口来执行所有的数据库操作。 在Spring Boot中,通常使…...
基于KZG多项式承诺方案的RLN
1. 引言 RLN——Rate-Limiting Nullifier为PSE团队主导的项目,源自: Barry White Hat 2019年博客 Semaphore RLN, rate limiting nullifier for spam prevention in anonymous p2p setting RLN(Rate-Limiting Nullifier)是一种…...
《站在巨人的肩膀上学习Java》
Java从诞生距今已经有28年了,在这段时间里,随着Java版本的不断迭代,Java新特性的不断出现,使得Java被使用的越来越广泛。在工程界Java语言一直是大家最喜欢的语言之一,Java一直排行在编程语言热门程度的前3名。 可想而…...
敏捷ACP.敏捷估计与规划.Mike Cohn.
第一部分 传统规划失败的原因 vs 敏捷规划有效的原因 传统的项目规划方式往往会让我们失望。要回答-一个 新产品的范围/进度/资源的组合问题,传统规划过程不一定会产生令人非常满意的答案和最终产品。以下- -些论据可以支持这个结论: ●大约2/3的项目会显著超…...
[创新工具和方法论]-01- DOE课程基础知识
文章目录 1.DOE实验设计的介绍1.1 什么是实验设计DOE?1.2 DOE的优势有哪些?1.3 如何开展DoE研究?步骤 2.DOE实验培训3.数据分析步骤4.实验的随机化5.偏差6.R方 相关系数假设检验 7.三因子二水平全因子设计 1.DOE实验设计的介绍 实验设计是一种安排实验和分析实验数…...
LeetCode-1033. 移动石子直到连续
题目链接 LeetCode-1033. 移动石子直到连续 题目描述 题解 题解一(Java) 作者:仲景 这题目挺难懂的,得画画图才能更好的理解 这也是LeetCode的尿性,习惯了,非得整这种别人看不懂的鸟语 你可以这样理解&a…...
JVM调优入门指南:掌握步骤、参数和场景
前言 作为Java开发者,我们经常需要优化应用的性能,其中JVM调优是非常重要的一部分。在本文中,我们将介绍JVM调优的一般步骤和方法,了解JVM调优参数,如堆大小、新生代比例、GC算法等参数的作用和配置方式,并…...
基于JSP+MySQL的跳蚤市场网站设计与开发
摘 要 在当今社会,网络信息已经不是什么很陌生的词汇,每天都在这个信息时代里生活着并且享受着它带来的与众不同。网络购物可以说是飞速发展的,这种购物方式逐渐的影响着人们的衣食住行。所以利用计算机实现 跳蚤市场网站设计与开发势在必行。本网站是一个校园的跳蚤市场网…...
内网穿透NPS和宝塔Nginx配合使用,开启SSL访问本地局域网网络
并非为了教学,仅供自己记录,方便下次用。所以内容不会刻意花时间写的很细节详细。 1. 服务器NPS配置 NPS install安装后,配置文件会在其他位置,通过是 /etc/nps/nps.conf目录。 找到进行修改,主要修改的是http_proxy_p…...
ToLua框架
ToLua 是一个用于在 Unity 中为 Lua 提供 C# 语言绑定的框架。通过 ToLua,你可以方便地将 C# 代码暴露给 Lua 脚本,并在 Lua 脚本中调用 C# 类、方法和属性。 更新流程 原理:使用AssetBundle进行资源的更新,而由于lua运行时才编…...
Golang-常见数据结构Map
Map map 是一种特殊的数据结构:一种元素对(pair)的无序集合,pair 的一个元素是 key,对应的另一个元素是 value,所以这个结构也称为关联数组或字典。这是一种快速寻找值的理想结构:给定 key&…...
基于空间矢量脉宽调制(SVPWM)的并网逆变器研究(Simulink)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...
介绍tcpdump在centos中的使用方法
tcpdump是一款强大的命令行数据包分析器,支持多种过滤和抓包参数。下面将介绍tcpdump的常用抓包参数。当需要监控CentOS系统的网络流量或者进行网络故障排查时,可以使用tcpdump来捕获数据包并进行分析。 下面介绍在CentOS中使用tcpdump的方法࿱…...
机器学习实战:Python基于DT决策树模型进行分类预测(六)
文章目录 1 前言1.1 决策树的介绍1.2 决策树的应用 2 Scikit-learn数据集演示2.1 导入函数2.2 导入数据2.3 建模2.4 评估模型2.5 可视化决策树2.6 优化模型2.7 可视化优化模型 3 讨论 1 前言 1.1 决策树的介绍 决策树(Decision Tree,DT)是一…...
毕设程序java车险理赔管理系统 基于SpringBoot的车辆保险智能定损与理赔平台 汽车保险全流程数字化管理与在线理赔系统
毕设程序java车险理赔管理系统b8xa28me (配套有源码 程序 mysql数据库 论文) 本套源码可以在文本联xi,先看具体系统功能演示视频领取,可分享源码参考。随着汽车保有量的持续攀升和交通事故频发,车险理赔作为连接车主权益保障与保险…...
什么是Spring Boot 应用开发?
一、引言 在当今的软件开发领域,Java 依然占据着重要的地位,而 Spring Boot 作为 Java 生态系统中极具影响力的框架,极大地简化了企业级应用的开发流程,提升了开发效率和应用的可维护性。它基于 Spring 框架构建,通过约…...
YOLO模型训练管道内缺陷数据集 下水管内部损害缺陷数据集 管道下水道损害检测数据集 6类 ‘树根‘, ‘沉积物‘, ‘裂缝‘, ‘垃圾‘, ‘错口‘, ‘穿入 目标检测使用
损害检测数据集 6类 ‘树根’, ‘沉积物’, ‘裂缝’, ‘垃圾’, ‘错口’, 穿入 目标检测使用数据集介绍 数据集概述 数据集名称:Pipe Sewer Damage Detection Dataset (PSDDD) 数据类型:RGB图像 目标类别:6类管道下水道损害 图像数量&#…...
Harmonyos应用实例98:约分和通分工具
应用实例八:约分和通分工具 知识点:掌握约分和通分的方法。 功能:输入一个分数,工具可以一步步展示其约分过程(找出公因数,分子分母同时除以公因数)。输入两个分数,工具可以展示通分过程(找最小公倍数,化为同分母分数)。 // SimplifyCommonDenominator.ets @Entr…...
STEP3-VL-10B入门指南:支持SVG/HEIC/WebP等非常规格式解析
STEP3-VL-10B入门指南:支持SVG/HEIC/WebP等非常规格式解析 你是不是经常遇到这种情况:手头有一堆SVG矢量图、HEIC苹果照片或者WebP网页图片,想找个AI模型来分析一下,结果发现大多数模型只认识常见的JPG和PNG格式?别担…...
立创开源:基于STM32F103的FOC驱动器设计(芙宁娜·彩印版)——硬件电路与软件实现详解
立创开源:基于STM32F103的FOC驱动器设计(芙宁娜彩印版)——硬件电路与软件实现详解 最近在做一个云台项目,需要驱动一个小功率的无刷电机,并且要实现精准的位置和速度控制。找了一圈,发现市面上的驱动器要么…...
虚拟显示驱动如何突破硬件限制?Parsec VDD全场景应用指南
虚拟显示驱动如何突破硬件限制?Parsec VDD全场景应用指南 【免费下载链接】parsec-vdd ✨ Virtual super display, upto 4K 2160p240hz 😎 项目地址: https://gitcode.com/gh_mirrors/pa/parsec-vdd 在当今数字化工作与娱乐场景中,物理…...
Tftpd64网络服务工具实战指南:从基础部署到企业级应用全攻略
Tftpd64网络服务工具实战指南:从基础部署到企业级应用全攻略 【免费下载链接】tftpd64 The working repository of the famous TFTP server. 项目地址: https://gitcode.com/gh_mirrors/tf/tftpd64 Tftpd64是一款集成TFTP(Trivial File Transfer …...
LongCat-Image-Editn部署教程:低配环境(8G RAM+16G GPU)稳定运行实录
LongCat-Image-Editn部署教程:低配环境(8G RAM16G GPU)稳定运行实录 1. 环境准备与快速部署 LongCat-Image-Editn是一个强大的图像编辑模型,它最大的特点是能用一句话就能修改图片,而且只改你想改的部分,…...
vmbox虚拟机安装rknn-toolkit2,遇到illegal hardware instruction (core dumped) 需要avx指令支持
虚拟机中查看cat /proc/cpuinfo | grep avx 没输出就是没有,如果真机cpu是支持的(用CPU-Z查看是否支持),那请尝试按照以下处理: 步骤一:关闭Hyper-V虚拟 步骤二:vmbox虚拟机 启用嵌套VT-x/AMD-v 参考 步骤三:在cmd中执行bcdedi…...
