ThreadLocal的应用场景
ThreadLocal介绍
ThreadLocal为每个线程都提供了变量的副本,使得每个线程访问各自独立的对象,这样就隔离了多个线程对数据的共享,使得线程安全。ThreadLocal有如下方法:
| 方法声明 | 描述 |
| public void set(T value) | 设置当前线程绑定的局部变量 |
| public T get() | 获取当前线程绑定的局部变量 |
| public void remove() | 移除当前线程绑定的局部变量 |
| protected Object initialValue() | 初始化值 |
ThreadLocal应用场景
场景一:每个线程需要一个独享的对象(典型的需要使用的类就是 SimpleDateFormat,因为它是线程不安全的)
package com.gingko.threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;//线程不安全
public class DateUtils1 {private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static String formatDate(long seconds) {Date date = new Date(seconds*1000);String format = dateFormat.format(date);return format;}public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(10);for(int i=0;i<100;i++) {int finalI = i;/*** 10个线程共享1个SimpleDateFormat,会发生线程安全问题,运行结果出现相同的时间*/executorService.submit(()-> {try {String formatDate = formatDate(finalI);System.out.println(formatDate);Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}});}executorService.shutdown();}
}
运行结果:

分析:线程池创建了10个线程处理100个任务,10个线程共享1个SimpleDateFormat,发生了线程安全问题,运行结果出现相同的时间。
改进版本:加锁机制
package com.gingko.threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//使用锁机制,可以解决线程安全问题,多线程时等待,执行效率较低
public class DateUtils2 {private static SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//使用锁机制,多线程时等待,执行效率较低public static synchronized String formatDate(long seconds) {Date date = new Date(seconds*1000);String format = dateFormat.format(date);return format;}public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(10);for(int i=0;i<100;i++) {int finalI = i;/*** 10个线程共享1个SimpleDateFormat*/executorService.submit(()-> {try {String formatDate = formatDate(finalI);System.out.println(formatDate);Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}});}executorService.shutdown();}
}
运行结果:

分析:线程池创建了10个线程处理100个任务,10个线程共享1个SimpleDateFormat,在formatDate方法上加了锁,使得多个线程同时执行此方法时需要排队等待获取锁,执行结果没有问题,但是由于要等待获取锁,执行效率低。
改进版本:使用ThreadLocal
package com.gingko.threadlocal;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
//使用threadlocal,使得每个线程有各个独立的SimpleDateFormat
public class DateUtils3 {//使用threadlocal,使得每个线程有各个独立的SimpleDateFormatprivate static ThreadLocal<SimpleDateFormat> dateFormatThreadLocal = ThreadLocal.withInitial(()-> {return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");});public static String formatDate(long seconds) {Date date = new Date(seconds*1000);String format = dateFormatThreadLocal.get().format(date);return format;}public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(10);for(int i=0;i<100;i++) {int finalI = i;/*** 10个线程有各自独立的SimpleDateFormat,不会发生线程安全问题*/executorService.submit(()-> {try {String formatDate = formatDate(finalI);System.out.println(formatDate);Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}});}executorService.shutdown();}
}
运行结果:
分析:线程池创建了10个线程处理100个任务,10个线程独占各自的SimpleDateFormat,执行结果没有问题,没有锁机制,执行效率高。
场景二:替代参数链传递

上图中通过前台获取到用户信息后,一路向下传递,假设方法A、B、C都需要用户信息,一种方式是A、B、C方法都接收用户信息作为入参(非常繁琐),一种方式是将用户信息放入ThreadLocal中,这样线程链上的所有方法都可以获取到用户信息,不用在方法A、B、C显式的指定User Info的入参,类似的应用场景还有:前台传递的分页参数等。
package com.gingko.threadlocal;import com.gingko.entity.Student;public class TransferParam {//线程中放入student信息,整个线程链路上都可以获取student信息private static ThreadLocal<Student> studentThreadLocal = new ThreadLocal<>();public static void main(String[] args) {TransferParam transferParam = new TransferParam();transferParam.setParam();}public void setParam() {Student student = new Student("1","张三",18,"001");studentThreadLocal.set(student);new ServiceA().getParam();}class ServiceA {public void getParam() {Student student = studentThreadLocal.get();System.out.println("ServiceA:" + student);new ServiceB().getParam();}}class ServiceB {public void getParam() {Student student = studentThreadLocal.get();System.out.println("ServiceB:" + student);//线程链路的最后删除threadlocal信息,防止发生内存泄露studentThreadLocal.remove();}}
}
运行结果:

从结果上看出:在方法setParam设置了ThreadLocal的变量student,在其线程调用的链条上方法:ServiceA.getParam 和ServiceB.getParam 都可以获取到student
注意:在线程链最后的方法上记得调用ThreadLocal的remove方法,不然会出现内存泄漏的风险,这块内容后续的章节会介绍。
相关文章:
ThreadLocal的应用场景
ThreadLocal介绍 ThreadLocal为每个线程都提供了变量的副本,使得每个线程访问各自独立的对象,这样就隔离了多个线程对数据的共享,使得线程安全。ThreadLocal有如下方法: 方法声明 描述public void set(T value)设置当前线程绑定的…...
Python--plt.errorbar学习笔记
plt.errorbar 是 Matplotlib 库中的一个函数,用于绘制带有误差条的图形。下面给出的代码行的详细解释: import numpy as np from scipy.special import kv, erfc from scipy.integrate import dblquad import matplotlib.pyplot as plt import scipy.in…...
文件信息类QFileInfo
常用方法: 构造函数 //参数:文件的绝对路径或相对路径 [explicit] QFileInfo::QFileInfo(const QString &path) 设置文件路径 可构造一个空的QFileInfo的对象,然后设置路径 //参数:文件的绝对路径或相对路径 void QFileI…...
堆排序(C++实现)
参考: 面试官:请写一个堆排序_哔哩哔哩_bilibiliC实现排序算法_c从小到大排序-CSDN博客 堆的基本概念 堆排实际上是利用堆的性质来进行排序。堆可以看做一颗完全二叉树。 堆分为两类: 最大堆(大顶堆):除根…...
Qt中加入UI文件
将 UI 文件整合到 Qt 项目 使用 Qt Designer 创建 UI 文件: 在 Qt Creator 中使用 Qt Designer 创建 UI 文件,设计所需的界面。确保在设计中包含所需的控件(如按钮、文本框等),并为每个控件设置明确的对象名称…...
Redisson使用全解
redisson使用全解——redisson官方文档注释(上篇)_redisson官网中文-CSDN博客 redisson使用全解——redisson官方文档注释(中篇)-CSDN博客 redisson使用全解——redisson官方文档注释(下篇)_redisson官网…...
Go4 和对 Go 的贡献
本篇内容是根据2017年4月份Go4 and Contributing to Go音频录制内容的整理与翻译, Brad Fitzpatrick 加入节目谈论成为开源 Go 的代言人、让社区参与 bug 分类、Go 的潜在未来以及其他有趣的 Go 项目和新闻。 过程中为符合中文惯用表达有适当删改, 版权归原作者所有. Erik St…...
区间动态规划
区间动态规划(Interval DP)是动态规划的一种重要变种,特别适用于解决一类具有区间性质的问题。典型的应用场景是给定一个区间,要求我们在满足某些条件下进行最优划分或合并。本文将从区间DP的基本思想、常见问题模型以及算法实现几…...
什么情况下需要使用电压探头
高压探头是一种专门设计用于测量高压电路或设备的探头,其作用是在电路测试和测量中提供安全、准确的信号捕获,并确保操作人员的安全。这些探头通常用于测量高压电源、变压器、电力系统、医疗设备以及其他需要处理高电压的设备或系统。 而高压差分探头差分…...
数据结构——八大排序(下)
数据结构中的八大排序算法是计算机科学领域经典的排序方法,它们各自具有不同的特点和适用场景。以下是这八大排序算法的详细介绍: 五、选择排序(Selection Sort) 核心思想:每一轮从未排序的元素中选择最小࿰…...
Linux系统:Ubuntu上安装Chrome浏览器
Ubuntu系统版本:23.04 在Ubuntu系统上安装Google Chrome浏览器,可以通过以下步骤进行: 终端输入以下命令,先更新软件源: sudo apt update 或 sudo apt upgrade终端输入以下命令,下载最新的Google Chrome .…...
Redis位图BitMap
一、为什么使用位图? 使用位图能有效实现 用户签到 等行为,用数据库表记录签到,将占用很多存储;但使用 位图BitMap,就能 大大减少存储占用 二、关于位图 本质上是String类型,最小长度8位(一个字…...
YOLOv11改进策略【卷积层】| ParNet 即插即用模块 二次创新C3k2
一、本文介绍 本文记录的是利用ParNet中的基础模块优化YOLOv11的目标检测网络模型。 ParNet block是一个即插即用模块,能够在不增加深度的情况下增加感受野,更好地处理图像中的不同尺度特征,有助于网络对输入数据更全面地理解和学习,从而提升网络的特征提取能力和分类性能…...
学习threejs,网格深度材质MeshDepthMaterial
👨⚕️ 主页: gis分享者 👨⚕️ 感谢各位大佬 点赞👍 收藏⭐ 留言📝 加关注✅! 👨⚕️ 收录于专栏:threejs gis工程师 文章目录 一、🍀前言1.1 ☘️网格深度材质MeshDepthMate…...
算法时间、空间复杂度(二)
目录 大O渐进表示法 一、时间复杂度量级的判断 定义: 例一:执行2*N+1次 例二:执行MN次 例三:执行已知次数 例四:存在最好情况和最坏情况 顺序查找 冒泡排序 二分查找 例五:阶乘递归 编辑 例…...
高级java每日一道面试题-2024年10月11日-数据库篇[Redis篇]-Redis都有哪些使用场景?
如果有遗漏,评论区告诉我进行补充 面试官: Redis都有哪些使用场景? 我回答: Redis 是一个开源的、基于键值对的数据结构存储系统,,它支持多种数据类型,包括字符串、散列、列表、集合和有序集合。它可以用作数据库、缓存和消息中间件。由于…...
0047__【python打包分发工具】setuptools详解
【python打包分发工具】setuptools详解-CSDN博客...
自定义拦截器处理token
目录 1、WebConfig 配置类 2、TSUserContext 把用户信息放到context中 3、自定义拦截器 4、在controller中可以使用 5、参考链接 1、WebConfig 配置类 @Configuration public class WebConfig implements WebMvcConfigurer {@Autowiredprivate AccessControlInterceptor …...
Scrapy | 使用Scrapy进行数据建模和请求
scrapy数据建模与请求 数据建模1.1 为什么建模1.2 如何建模1.3如何使用模板类1.4 开发流程总结 目标: 1.应用在scrapy项目中进行建模 2.应用构造Request对象,并发送请求 3.应用利用meta参数在不同的解析函数中传递数据 数据建模 | 通常在做项目的过程中…...
学习笔记——交换——STP(生成树)基本概念
三、基本概念 1、桥ID/网桥ID (Bridege ID,BID) 每一台运行STP的交换机都拥有一个唯一的桥ID(BID),BID(Bridge ID/桥ID)。在STP里我们使用不同的桥ID标识不同的交换机。 (2)BID(桥ID)组成 BID(桥ID)组成(8个字节):由16位(2字节)的桥优先级…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...
centos 7 部署awstats 网站访问检测
一、基础环境准备(两种安装方式都要做) bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats࿰…...
大数据零基础学习day1之环境准备和大数据初步理解
学习大数据会使用到多台Linux服务器。 一、环境准备 1、VMware 基于VMware构建Linux虚拟机 是大数据从业者或者IT从业者的必备技能之一也是成本低廉的方案 所以VMware虚拟机方案是必须要学习的。 (1)设置网关 打开VMware虚拟机,点击编辑…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
Python爬虫(一):爬虫伪装
一、网站防爬机制概述 在当今互联网环境中,具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类: 身份验证机制:直接将未经授权的爬虫阻挡在外反爬技术体系:通过各种技术手段增加爬虫获取数据的难度…...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...
