当前位置: 首页 > news >正文

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为每个线程都提供了变量的副本&#xff0c;使得每个线程访问各自独立的对象&#xff0c;这样就隔离了多个线程对数据的共享&#xff0c;使得线程安全。ThreadLocal有如下方法&#xff1a; 方法声明 描述public void set(T value)设置当前线程绑定的…...

Python--plt.errorbar学习笔记

plt.errorbar 是 Matplotlib 库中的一个函数&#xff0c;用于绘制带有误差条的图形。下面给出的代码行的详细解释&#xff1a; import numpy as np from scipy.special import kv, erfc from scipy.integrate import dblquad import matplotlib.pyplot as plt import scipy.in…...

文件信息类QFileInfo

常用方法&#xff1a; 构造函数 //参数&#xff1a;文件的绝对路径或相对路径 [explicit] QFileInfo::QFileInfo(const QString &path) 设置文件路径 可构造一个空的QFileInfo的对象&#xff0c;然后设置路径 //参数&#xff1a;文件的绝对路径或相对路径 void QFileI…...

堆排序(C++实现)

参考&#xff1a; 面试官&#xff1a;请写一个堆排序_哔哩哔哩_bilibiliC实现排序算法_c从小到大排序-CSDN博客 堆的基本概念 堆排实际上是利用堆的性质来进行排序。堆可以看做一颗完全二叉树。 堆分为两类&#xff1a; 最大堆&#xff08;大顶堆&#xff09;&#xff1a;除根…...

Qt中加入UI文件

将 UI 文件整合到 Qt 项目 使用 Qt Designer 创建 UI 文件&#xff1a; 在 Qt Creator 中使用 Qt Designer 创建 UI 文件&#xff0c;设计所需的界面。确保在设计中包含所需的控件&#xff08;如按钮、文本框等&#xff09;&#xff0c;并为每个控件设置明确的对象名称&#xf…...

Redisson使用全解

redisson使用全解——redisson官方文档注释&#xff08;上篇&#xff09;_redisson官网中文-CSDN博客 redisson使用全解——redisson官方文档注释&#xff08;中篇&#xff09;-CSDN博客 redisson使用全解——redisson官方文档注释&#xff08;下篇&#xff09;_redisson官网…...

Go4 和对 Go 的贡献

本篇内容是根据2017年4月份Go4 and Contributing to Go音频录制内容的整理与翻译, Brad Fitzpatrick 加入节目谈论成为开源 Go 的代言人、让社区参与 bug 分类、Go 的潜在未来以及其他有趣的 Go 项目和新闻。 过程中为符合中文惯用表达有适当删改, 版权归原作者所有. Erik St…...

区间动态规划

区间动态规划&#xff08;Interval DP&#xff09;是动态规划的一种重要变种&#xff0c;特别适用于解决一类具有区间性质的问题。典型的应用场景是给定一个区间&#xff0c;要求我们在满足某些条件下进行最优划分或合并。本文将从区间DP的基本思想、常见问题模型以及算法实现几…...

什么情况下需要使用电压探头

高压探头是一种专门设计用于测量高压电路或设备的探头&#xff0c;其作用是在电路测试和测量中提供安全、准确的信号捕获&#xff0c;并确保操作人员的安全。这些探头通常用于测量高压电源、变压器、电力系统、医疗设备以及其他需要处理高电压的设备或系统。 而高压差分探头差分…...

数据结构——八大排序(下)

数据结构中的八大排序算法是计算机科学领域经典的排序方法&#xff0c;它们各自具有不同的特点和适用场景。以下是这八大排序算法的详细介绍&#xff1a; 五、选择排序&#xff08;Selection Sort&#xff09; 核心思想&#xff1a;每一轮从未排序的元素中选择最小&#xff0…...

Linux系统:Ubuntu上安装Chrome浏览器

Ubuntu系统版本&#xff1a;23.04 在Ubuntu系统上安装Google Chrome浏览器&#xff0c;可以通过以下步骤进行&#xff1a; 终端输入以下命令&#xff0c;先更新软件源&#xff1a; sudo apt update 或 sudo apt upgrade终端输入以下命令&#xff0c;下载最新的Google Chrome .…...

Redis位图BitMap

一、为什么使用位图&#xff1f; 使用位图能有效实现 用户签到 等行为&#xff0c;用数据库表记录签到&#xff0c;将占用很多存储&#xff1b;但使用 位图BitMap&#xff0c;就能 大大减少存储占用 二、关于位图 本质上是String类型&#xff0c;最小长度8位&#xff08;一个字…...

YOLOv11改进策略【卷积层】| ParNet 即插即用模块 二次创新C3k2

一、本文介绍 本文记录的是利用ParNet中的基础模块优化YOLOv11的目标检测网络模型。 ParNet block是一个即插即用模块,能够在不增加深度的情况下增加感受野,更好地处理图像中的不同尺度特征,有助于网络对输入数据更全面地理解和学习,从而提升网络的特征提取能力和分类性能…...

学习threejs,网格深度材质MeshDepthMaterial

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️网格深度材质MeshDepthMate…...

算法时间、空间复杂度(二)

目录 大O渐进表示法 一、时间复杂度量级的判断 定义&#xff1a; 例一&#xff1a;执行2*N&#xff0b;1次 例二&#xff1a;执行MN次 例三&#xff1a;执行已知次数 例四:存在最好情况和最坏情况 顺序查找 冒泡排序 二分查找 例五&#xff1a;阶乘递归 ​编辑 例…...

高级java每日一道面试题-2024年10月11日-数据库篇[Redis篇]-Redis都有哪些使用场景?

如果有遗漏,评论区告诉我进行补充 面试官: Redis都有哪些使用场景? 我回答: Redis 是一个开源的、基于键值对的数据结构存储系统&#xff0c;&#xff0c;它支持多种数据类型&#xff0c;包括字符串、散列、列表、集合和有序集合。它可以用作数据库、缓存和消息中间件。由于…...

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 开发流程总结 目标&#xff1a; 1.应用在scrapy项目中进行建模 2.应用构造Request对象&#xff0c;并发送请求 3.应用利用meta参数在不同的解析函数中传递数据 数据建模 | 通常在做项目的过程中…...

学习笔记——交换——STP(生成树)基本概念

三、基本概念 1、桥ID/网桥ID (Bridege ID&#xff0c;BID) 每一台运行STP的交换机都拥有一个唯一的桥ID(BID)&#xff0c;BID(Bridge ID/桥ID)。在STP里我们使用不同的桥ID标识不同的交换机。 (2)BID(桥ID)组成 BID(桥ID)组成(8个字节)&#xff1a;由16位(2字节)的桥优先级…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

【位运算】消失的两个数字(hard)

消失的两个数字&#xff08;hard&#xff09; 题⽬描述&#xff1a;解法&#xff08;位运算&#xff09;&#xff1a;Java 算法代码&#xff1a;更简便代码 题⽬链接&#xff1a;⾯试题 17.19. 消失的两个数字 题⽬描述&#xff1a; 给定⼀个数组&#xff0c;包含从 1 到 N 所有…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

tree 树组件大数据卡顿问题优化

问题背景 项目中有用到树组件用来做文件目录&#xff0c;但是由于这个树组件的节点越来越多&#xff0c;导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多&#xff0c;导致的浏览器卡顿&#xff0c;这里很明显就需要用到虚拟列表的技术&…...

ip子接口配置及删除

配置永久生效的子接口&#xff0c;2个IP 都可以登录你这一台服务器。重启不失效。 永久的 [应用] vi /etc/sysconfig/network-scripts/ifcfg-eth0修改文件内内容 TYPE"Ethernet" BOOTPROTO"none" NAME"eth0" DEVICE"eth0" ONBOOT&q…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

6.9-QT模拟计算器

源码: 头文件: widget.h #ifndef WIDGET_H #define WIDGET_H#include <QWidget> #include <QMouseEvent>QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACEclass Widget : public QWidget {Q_OBJECTpublic:Widget(QWidget *parent nullptr);…...

pgsql:还原数据库后出现重复序列导致“more than one owned sequence found“报错问题的解决

问题&#xff1a; pgsql数据库通过备份数据库文件进行还原时&#xff0c;如果表中有自增序列&#xff0c;还原后可能会出现重复的序列&#xff0c;此时若向表中插入新行时会出现“more than one owned sequence found”的报错提示。 点击菜单“其它”-》“序列”&#xff0c;…...

PostgreSQL 与 SQL 基础:为 Fast API 打下数据基础

在构建任何动态、数据驱动的Web API时&#xff0c;一个稳定高效的数据存储方案是不可或缺的。对于使用Python FastAPI的开发者来说&#xff0c;深入理解关系型数据库的工作原理、掌握SQL这门与数据库“对话”的语言&#xff0c;以及学会如何在Python中操作数据库&#xff0c;是…...