ThreadLocal 的原理和使用场景

1.ThreadLocal是什么

ThreadLocal 是 Java 提供的一个用于线程存储本地变量的类。它为每个线程提供独立的变量副本,确保变量在多线程环境下的线程安全。每个线程访问 ThreadLocal 时,都会有自己专属的变量副本,互不干扰,避免了并发访问时共享变量的竞争问题。

主要作用

  • 线程隔离:ThreadLocal 提供了线程之间的数据隔离。当多个线程操作同一个对象时,可以通过 ThreadLocal 为每个线程分配一个独立的变量副本,从而避免多线程间的变量共享导致的数据不一致问题。

  • 解决并发问题:在高并发场景下,ThreadLocal 可以用来存储线程相关的状态信息,这样可以减少线程间的竞争,提高程序的并发性能。

  • 可以跨层,跨类跨方法传递变量

  • 简化代码:使用 ThreadLocal 可以简化多线程环境下的编程模型,使得线程局部变量的访问变得像访问普通变量一样简单。

2.ThreadLocal的工作原理

ThreadLocal 的核心机制是为每个线程创建一个独立的变量副本,并且这个副本是存储在线程自身的内部结构中,而不是 ThreadLocal 实例中。它主要依赖于 Thread 类中的 ThreadLocalMap 来实现这一功能。

1. ThreadLocalMap每个 Thread 对象内部维护了一个 ThreadLocalMap,用于存储线程的本地变量。

ThreadLocalMap 是一个类似于哈希表的结构,其中 ThreadLocal 对象作为键,线程的本地变量副本作为值。

  • 每次线程调用 ThreadLocal.set() 方法时,实际上是将变量存储到该线程的 ThreadLocalMap 中,ThreadLocal 实例作为键。

  • 当线程调用 ThreadLocal.get() 方法时,会从当前线程的 ThreadLocalMap 中读取与 ThreadLocal 对象相关联的值。

举例说明:


public class ThreadLocalExample {// 创建一个 ThreadLocal 变量,用于存储每个线程的本地变量副本private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 创建第一个线程Thread thread1 = new Thread(() -> {// 设置线程本地变量threadLocal.set(100);System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get());}, "Thread-1");// 创建第二个线程Thread thread2 = new Thread(() -> {// 设置线程本地变量threadLocal.set(200);System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get());}, "Thread-2");// 启动两个线程thread1.start();thread2.start();}
}

解释

  • ThreadLocal 为每个线程提供了它自己的变量副本。Thread-1 设置了值 100Thread-2 设置了值 200。尽管它们都使用了相同的 ThreadLocal 实例,但由于每个线程都有独立的变量副本,因此线程之间的数据互不影响。

  • 这就相当于每个线程维护了自己的 ThreadLocalMap,并在其中存储该 ThreadLocal 及其对应的变量值。

2. 弱引用的使用

ThreadLocalMap 使用了弱引用来引用 ThreadLocal 对象,这意味着如果某个 ThreadLocal 实例没有被其他对象强引用时,Java 垃圾回收器(GC)可以对其进行回收,避免内存泄漏。为了避免出现内存泄漏风险,开发者应该在使用完 ThreadLocal 变量后,主动调用 remove() 方法清理资源。

3. 主要方法

  • set(T value):将当前线程的局部变量值存储到 ThreadLocalMap 中。

  • get():获取当前线程的局部变量值。如果是第一次访问,没有值时,调用 initialValue() 设置默认值。

  • remove():移除当前线程的局部变量,如不移除,会一直在脑门上,占用内存空间,导致的内存泄漏。

3.ThreadLocal的使用场景

1.用户会话管理

  • 在处理 HTTP 请求时,每个线程都代表一个用户请求。可以使用 ThreadLocal 来存储每个线程的会话信息,如用户 ID、认证信息、角色等,保证线程间的会话信息独立。


public class UserSessionContext {private static final ThreadLocal<UserSession> contextHolder = new ThreadLocal<>();public static void set(UserSession session) {contextHolder.set(session);}public static UserSession get() {return contextHolder.get();}public static void remove() {contextHolder.remove();}
}


2.数据库连接管理

  • 在一些数据库操作中,每个线程可能需要维护一个数据库连接。通过 ThreadLocal,可以确保每个线程都有一个独立的数据库连接,避免了多个线程竞争同一个连接。


public class DBConnectionManager {private static ThreadLocal<Connection> connectionHolder = ThreadLocal.withInitial(() -> {// 创建并返回数据库连接return DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "user", "password");});public static Connection getConnection() {return connectionHolder.get();}public static void closeConnection() throws SQLException {Connection connection = connectionHolder.get();if (connection != null) {connection.close();}connectionHolder.remove();}
}

3.事务管理

  • 在事务管理中,可以通过 ThreadLocal 来确保每个线程拥有独立的事务状态,从而保证事务的原子性和隔离性。

  • Spring 的 TransactionSynchronizationManager 就是通过 ThreadLocal 来存储当前线程的事务上下文信息。

4.日志记录

  • 可以在日志系统中使用 ThreadLocal 存储线程级别的上下文信息,比如 requestIdtraceId,这样在整个请求链中都可以记录统一的上下文信息,方便追踪日志。

public class RequestContext {private static  ThreadLocal<String> traceId= ThreadLocal.withInitial(IdUtil::fastUUID);public static String getTraceId() {return requestId.get();}public static void clear() {requestId.remove();}
}

4.ThreadLocal的优缺点

优点:

  1. 线程安全ThreadLocal 提供了每个线程自己的独立变量副本,确保线程之间互不干扰,从而避免了线程安全问题。

  2. 简化编程模型:避免了显式的同步锁的使用,简化了并发编程。

  3. 数据隔离:可以为每个线程提供单独的上下文环境,方便跨层传递数据,避免了参数传递的复杂性。

缺点:

  1. 内存泄漏:如果在线程池中使用 ThreadLocal 而没有及时调用 remove(),线程的局部变量可能不会被回收,导致内存泄漏问题。由于线程池中的线程会被复用,因此必须显式清理。

  2. 滥用风险ThreadLocal 在大量使用时,可能隐藏代码的上下文依赖,导致系统难以调试和维护。

5.注意事项

避免内存泄漏:当线程执行完成后,最好调用 ThreadLocal.remove() 方法清除局部变量,防止线程池复用时出现内存泄漏。

try {// 使用 ThreadLocal
} finally {threadLocal.remove();
}
  • 使用默认初始值:如果每个线程访问时需要有特定的初始值,最好重写 initialValue() 方法,确保每次 get() 时有正确的默认值。

  • 跨线程通信的局限性ThreadLocal 变量只对创建它的线程可见,如果需要在不同线程之间共享数据,ThreadLocal 并不是适合的工具。

6.总结

  • ThreadLocal 提供了线程本地存储,保证每个线程都有自己独立的变量副本,适用于场景如会话管理、数据库连接、事务管理、跨层数据传参等。

  • 它通过 ThreadLocalMap 为每个线程存储本地变量,并通过弱引用机制来避免内存泄漏。

  • 使用 ThreadLocal 可以减少锁的使用和同步开销,但需要谨慎处理内存泄漏风险。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

qt QColorDialog详解

1、概述 QColorDialog是Qt框架中的一个对话框类&#xff0c;专门用于让用户选择颜色。它提供了一个标准的颜色选择界面&#xff0c;其中包括基本的颜色选择器&#xff08;如调色板和颜色轮&#xff09;、自定义颜色输入区域以及预定义颜色列表。QColorDialog支持RGB、HSV和十六…

关于Redis

Redis 基础 什么是 Redis&#xff1f; Redis &#xff08;REmote DIctionary Server&#xff09;是一个基于 C 语言开发的开源 NoSQL 数据库&#xff08;BSD 许可&#xff09;。与传统数据库不同的是&#xff0c;Redis 的数据是保存在内存中的&#xff08;内存数据库&#xf…

linux nvidia/cuda安装

1.查看显卡型号 lspci |grep -i vga2.nvidia安装 2.1在线安装 终端输入&#xff08;当显卡插上之后&#xff0c;系统会有推荐的安装版本&#xff09; ubuntu-drivers devices可得到如下内容 vendor : NVIDIA Corporation model : TU104GL [Tesla T4] driver : nvid…

uniapp 实现瀑布流

效果演示 组件下载 瀑布流布局-waterfall - DCloud 插件市场

了解聚簇索引和非聚簇索引

在关系型数据库中,索引是提高查询效率的重要手段。索引类似于书籍中的目录,能够帮助数据库快速定位到所需的数据。而在数据库中,最常用的两种索引类型是聚簇索引(Clustered Index)和非聚簇索引(Non-clustered Index)。本文将详细介绍这两种索引类型,帮助读者更好地理解…

CODESYS可视化桌面屏保-动态气泡制作详细案例

#一个用于可视化(HMI)界面的动态屏保的详细制作案例程序# 前言: 在工控自动化设备上,为了防止由于人为误触发或操作引起的故障,通常在触摸屏(HMI)增加屏幕保护界面,然而随着PLC偏IT化的发展,在控制界面上的美观程度也逐渐向上位机或网页前端方面发展,本篇模仿Windows…

【数据结构 队列】超详细理解例题

数据结构 队列 前言队列的定义队列的概念队列的基本操作 队列用C语言实现Queue.hQueue.ctext.c 队列 VS 栈队列的应用 你好&#xff0c;这里是新人 Sunfor 这篇是我最近对于数据结构 队列的学习心得和错题整理 有任何错误欢迎指正&#xff0c;欢迎交流&#xff01; 会持续更新…

VSCode + linux 远程免密登录

目录 一. VS Code端1. 安装插件Remote - SSH2. 配置config文件3. 公钥生成 二、远程服务器端1. 将生成的公钥发送到远程服务器 三、连接1. 准备就绪后&#xff0c;VSCode连接 一. VS Code端 1. 安装插件Remote - SSH 2. 配置config文件 Host H5WebHostName xx.xx.xx.xxUser ro…

简单分享一下淘宝商品数据自动化抓取的技术实现与挑战

在电子商务领域&#xff0c;数据是驱动决策的关键。淘宝作为国内最大的电商平台之一&#xff0c;其商品数据对电商从业者来说具有极高的价值。然而&#xff0c;从淘宝平台自动化抓取商品数据并非易事&#xff0c;涉及多重技术和法律挑战。本文将从技术层面分析实现淘宝商品数据…

初识网络编程

目录 前言相关名词解释应用层协议——HTTP传输层协议socketTCP帧头格式三次握手、四次挥手 UDPTCP的socket实现 参考博文 前言 刚碰到网络编程&#xff0c;会出现一堆协议、概念、这层次那技术的&#xff0c;头都大了&#xff0c;还是得总结总结…… 相关名词解释 ✨✨网络…

JRTPLIB中的RTPSession与OnSSRCCollision:深入解析SSRC冲突处理机制

JRTPLIB中的RTPSession与OnSSRCCollision:深入解析SSRC冲突处理机制 一、RTP与SSRC基础1.1 RTP简介1.2 SSRC的作用二、JRTPLIB与RTPSession2.1 JRTPLIB概述2.2 RTPSession类三、SSRC冲突与OnSSRCCollision3.1 SSRC冲突的原因3.2 OnSSRCCollision回调函数3.3 OnSSRCCollision的…

【数据集】【YOLO】【目标检测】口罩佩戴识别数据集 1971 张,YOLO佩戴口罩检测算法实战训练教程!

数据集介绍 【数据集】口罩佩戴检测数据集 1971 张&#xff0c;目标检测&#xff0c;包含YOLO/VOC格式标注。 数据集中包含1种分类&#xff1a;{0: face_mask}&#xff0c;佩戴口罩。 数据集来自国内外图片网站和视频截图。 检测场景为城市街道、医院、商场、机场、车站、办…

实测讯飞智作,一张照片定制属于自己的数字人

Datawhale亲测 AI应用&#xff1a;讯飞智作 只用一张照片&#xff0c;就可以定制属于自己的数字人。 这是大模型给数字人领域带来的最新震撼。 就在两周前的 AI 开发者 Talk 合肥站活动上&#xff0c;我们 Datawhale 的一名小伙伴玉鑫化身成数字人亮相大屏幕&#xff0c;为参加…

乡村景区一体化系统(门票,餐饮,便利店,果园,娱乐,停车收费

一、一体化优势 1. 提升游客体验&#xff1a;游客可以通过一个系统方便地完成各种消费和预订&#xff0c;无需在不同的地方分别处理&#xff0c;节省时间和精力&#xff0c;使游玩过程更加顺畅和愉快。 2. 提高管理效率&#xff1a;景区管理者能够在一个平台上集中管理多个业…

安卓编程最方便的读写资料类SharedPreferences,多个APP共享

本文介绍Android平台进行数据存储的五大方式,分别如下: 1 使用SharedPreferences存储数据 2 文件存储数据 3 SQLite数据库存储数据 4 使用ContentProvider存储数据 5 网络存储数据 下面详细讲解这五种方式的特点 第一种&#xff1a; 使用SharedPreferences存储数据 …

[Docker#1] 专栏前言 | 亿级高并发架构演进之路

目录 目标 一.前期演进 1. 单机架构 2. 应用数据分离架构 3. 应用集群架构 4. 读写分离/主从分离架构 5. 冷热分离架构 二. 架构 分布式数据库架构 微服务架构 容器编排架构 三. An Internet Application Architecture 理解 上层传输 框架 数据处理 主要思想 …

初识AI大模型,ollama使用,llama factory大模型微调,lama.cpp模型转换guff

最近了解了下生成式AI对话&#xff0c;下面是自己的一些尝试记录。 ollama 安装及使用 1、安装 我是在windows环境下安装的&#xff0c;很简单&#xff0c;访问&#xff1a;https://ollama.com/ &#xff0c;下载windows安装包&#xff0c;打开安装就行了。 cmd输入ollama -v检…

Mybatis、Mybatis-Plus 调用同一个组件的查询时遇到的坑

Mybatis、Mybatis-Plus 调用同一个组件的查询时遇到的坑 Mybais-plus配置了驼峰自动命名&#xff0c;所以不需要在SQL里转化查询。

ssm070基于SSM框架的校园代购服务订单管理系统的设计与实现+vue(论文+源码)_kaic

毕业设计 题 目&#xff1a; 校园代购服务订单管理系统 作 者&#xff1a; 学 号&#xff1a; 所属学院&#xff1a; 专业年级&#xff1a; 学校导师&#xff1a; 职 称&#xff1a; 班级导师&#xff1a; 职 称&#xff1a; 完成时间…

ECharts折线图背景渐变设置

目录 引入 1.在一个HTML文件中编写两个图表 2.渐变背景 引入 如何在一个HTML文件中编写两个图表&#xff1a;&#xff08;这个例子基于这个篇文章的基础&#xff09;一篇搞懂前端获取数据-CSDN博客 一个例子&#xff1a; 1.在一个HTML文件中编写两个图表 重点在于名字的不重…