在 SpringBoot+Tomcat 环境中 线程安全问题的根本原因以及哪些变量会存在线程安全的问题。

文章目录

  • 前言
  • Tomcat + SpringBoot
    • 单例加载
    • 结果分析
    • 多例加载:
    • 结果分析:
  • 哪些变量存在线程安全的问题?
    • 线程不安全
    • 线程安全
  • 总结

前言

本文带你去深入理解为什么在web环境中(Tomcat +SpringBoot)会存在多线程的问题以及哪些变量会存在线程安全的问题。

Tomcat + SpringBoot

首先我们来看下Tomcat的多线程处理模型:

1、Tomcat内部维护一个工作线程池
2、每个HTTP请求由Tomcat线程池中的一个工作线程处理
3、在高并发场景下,多个线程同时处理不同的HTTP请求

Spring Boot是如何去加载类的:

1、@Component 等注解修饰的类类会被 Spring 扫描到,并放入容器中成为 Bean
2、Spring容器中的Bean是单例的
3、所有请求共享同一个单例的Bean 类
4、所有线程获得的是同一个Bean 类的引用

正是由于Bean是单例的+每个HTTP请求一个工作线程处理
所以存在多个工作线程同时操作一个Bean实例,这样就导致了多线程竞争同一个资源,进而导致线程安全的问题。

实际例子去理解单例和多例加载:

单例加载

@Component
public class SingletonCounterService {private int count = 0;public void increase() {count++;System.out.println(Thread.currentThread().getName() + " count = " + count);}
}
@SpringBootTest
public class SingletonTest {@Autowiredprivate SingletonCounterService counter;@Testpublic void testMultiThreadSingleton() throws InterruptedException {Runnable task = () -> counter.increase();Thread t1 = new Thread(task, "T1");Thread t2 = new Thread(task, "T2");Thread t3 = new Thread(task, "T3");t1.start();t2.start();t3.start();t1.join();t2.join();t3.join();}
}
T1 count = 1
T2 count = 3
T3 count = 2

结果分析

多个线程同时操作同一个SingletonCounterService实例 内部的共享变量count 导致最后 count为3 而不是每一个都为1

多例加载:

@Component
@Scope("prototype")
public class PrototypeCounterService {private int count = 0;public void increase() {count++;System.out.println(Thread.currentThread().getName() + " count = " + count);}
}
@SpringBootTest
public class PrototypeTest {@Autowiredprivate ApplicationContext context;@Testpublic void testMultiThreadPrototype() throws InterruptedException {Runnable task = () -> {PrototypeCounterService counter = context.getBean(PrototypeCounterService.class);counter.increase(); // 每个线程是自己独立的 bean};Thread t1 = new Thread(task, "T1");Thread t2 = new Thread(task, "T2");Thread t3 = new Thread(task, "T3");t1.start();t2.start();t3.start();t1.join();t2.join();t3.join();}
}
T1 count = 1
T2 count = 1
T3 count = 1

结果分析:

每个线程拿到的是自己独立的 bean 实例,不共享count。

哪些变量存在线程安全的问题?

经过上面的分析,你已经知道了为什么会存在多线程的问题了吧。(多个线工作线程去操作同一个类实例)那么下一步就是去定位可能存在多线程安全的变量位置。

线程不安全

1、实例变量(成员变量):

@Service
public class UserService {private User currentUser;  // 不安全:多个线程可能同时修改currentUserprivate int counter = 0;  // 不安全:多线程递增count不是原子操作
}

2、非线程安全的实例变量

@Service
public class ReportService {private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");  // 不安全:SimpleDateFormat非线程安全private Map<String, Object> dataCache = new HashMap<>();  // 不安全:HashMap非线程安全private List<User> userCache = new ArrayList<>();  // 不安全:ArrayList是线程不安全的容器 相反vector是线程安全的
}

3、静态变量:

@Service
public class ConfigService {private static Map<String, String> globalConfig = new HashMap<>();  // 不安全:类变量本身就是跨所有实例共享 
}

线程安全

1、方法内的局部变量:

public void process() {int localCounter = 0;  // 安全:每个方法都有独立的方法栈 局部变量不共享List<String> localList = new ArrayList<>();  // 安全:局部变量
}

2、不可变(Immutable)实例变量

private final String apiUrl = "https://api.example.com";  // 安全:不可变
private final List<String> constants = Collections.unmodifiableList(Arrays.asList("A", "B"));  // 安全:不可变集合

3、线程安全的实例变量:

private AtomicInteger counter = new AtomicInteger(0);  // 安全:原子操作
private ConcurrentHashMap<String, User> userMap = new ConcurrentHashMap<>();  // 安全:并发集合

4、ThreadLocal变量

private ThreadLocal<User> currentUser = new ThreadLocal<>();  // 安全:线程隔离

总结

在Spring+Tomcat环境中,线程安全问题的根本原因是:

Tomcat使用线程池并发处理HTTP请求
Spring默认使用单例Bean
这导致多个线程并发访问同一个Service实例
当Service包含可变共享状态时,就会出现线程安全问题

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

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

相关文章

npm install 相关命令

npm install 相关命令 基本安装命令 # 安装 package.json 中列出的所有依赖 npm install npm i # 简写形式# 安装特定包 npm install <package-name># 安装特定版本 npm install <package-name><version>依赖类型选项 # 安装为生产依赖&#xff08;默认&…

贪心算法应用:最小反馈顶点集问题详解

贪心算法应用&#xff1a;最小反馈顶点集问题详解 1. 问题定义与背景 1.1 反馈顶点集定义 反馈顶点集(Feedback Vertex Set, FVS)是指在一个有向图中&#xff0c;删除该集合中的所有顶点后&#xff0c;图中将不再存在任何有向环。换句话说&#xff0c;反馈顶点集是破坏图中所…

BiliNote部署实践

​ 开源地址&#xff1a; https://github.com/JefferyHcool/BiliNote &#x1f680; 快速开始 1. 克隆仓库 git clone https://github.com/JefferyHcool/BiliNote.git cd BiliNote mv .env.example .env2. 启动后端&#xff08;FastAPI&#xff09; cd backend pip insta…

用go从零构建写一个RPC(4)--gonet网络框架重构+聚集发包

在追求高性能的分布式系统中&#xff0c;RPC 框架的底层网络能力和数据传输效率起着决定性作用。经过几轮迭代优化&#xff0c;我完成了第四版本的 RPC 框架。相比以往版本&#xff0c;这一版本的最大亮点在于 重写了底层网络框架 和 实现了发送端的数据聚集机制&#xff0c;这…

MySQL 高可用基石 - 复制监控与常见 HA 方案

MySQL 高可用基石 - 复制监控与常见 HA 方案 MySQL 复制核心原理 MySQL 复制允许数据从一个 MySQL 数据库服务器(称为主库 - Primary,旧称 Master)复制到一个或多个其他的 MySQL 服务器(称为从库 - Replica,旧称 Slave)。 复制的主要目的: 高可用性 (High Availability…

微信小程序(uniapp)自定义 TabBar

微信小程序&#xff08;uniapp&#xff09;自定义 TabBar 实现指南 在微信小程序开发中&#xff0c;TabBar 是底部导航栏的重要组件&#xff0c;但官方提供的 TabBar 样式和功能较为基础&#xff0c;无法满足所有项目的需求。本文将详细介绍如何在 uniapp 中实现自定义 TabBar…

MLP实战二:MLP 实现图像数字多分类

任务 实战&#xff08;二&#xff09;&#xff1a;MLP 实现图像多分类 基于 mnist 数据集&#xff0c;建立 mlp 模型&#xff0c;实现 0-9 数字的十分类 task: 1、实现 mnist 数据载入&#xff0c;可视化图形数字&#xff1b; 2、完成数据预处理&#xff1a;图像数据维度转换与…

BUUCTF[HCTF 2018]WarmUp 1题解

BUUCTF[HCTF 2018]WarmUp 1题解 分析解题过程代码审计主体函数CHECK函数&#xff1a; 构造payload 总结 分析 启动靶机&#xff0c;进入网址&#xff0c;是一张滑稽的表情包&#xff1a; 程序化F12查看源码&#xff1a; 发现注释内容&#xff0c;访问 url:/source.php得到…

大陆4D毫米波雷达ARS548调试

本文介绍了大陆ARS548毫米波雷达的调试与测试流程&#xff0c;主要包括以下内容&#xff1a; 设备参数&#xff1a;最大检测距离301m&#xff08;可调93-1514m&#xff09;&#xff0c;支持gPTP时间同步。 接线调试&#xff1a; Windows需使用USB-RJ45转换器 Linux可直接连接网…

TDengine 的 AI 应用实战——运维异常检测

作者&#xff1a; derekchen Demo数据集准备 我们使用公开的 NAB数据集 里亚马逊 AWS 东海岸数据中心一次 API 网关故障中&#xff0c;某个服务器上的 CPU 使用率数据。数据的频率为 5min&#xff0c;单位为占用率。由于 API 网关的故障&#xff0c;会导致服务器上的相关应用…

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…

《一生一芯》数字实验三:加法器与ALU

1. 实验目标 设计一个能实现如下功能的4位带符号位的 补码 ALU&#xff1a; Table 4 ALU 功能列表  功能选择 功能 操作 000 加法 AB 001 减法 A-B 010 取反 Not A 011 与 A and B 100 或 A or B 101 异或 A xor B 110 比较大小 If A<B then out1…

解读《网络安全法》最新修订,把握网络安全新趋势

《网络安全法》自2017年施行以来&#xff0c;在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂&#xff0c;网络攻击、数据泄露等事件频发&#xff0c;现行法律已难以完全适应新的风险挑战。 2025年3月28日&#xff0c;国家网信办会同相关部门起草了《网络安全…

Java并发编程实战 Day 10:原子操作类详解

【Java并发编程实战 Day 10】原子操作类详解 开篇 这是“Java并发编程实战”系列的第10天&#xff0c;我们将深入探讨原子操作类的核心技术——CAS原理、ABA问题以及原子类的实现机制。通过理论结合代码实践的方式&#xff0c;帮助读者理解并掌握如何在实际工作中高效使用原子…

瀚文机械键盘固件开发详解:HWKeyboard.h文件解析与应用

【手把手教程】从零开始的机械键盘固件开发&#xff1a;HWKeyboard.h详解 前言 大家好&#xff0c;我是键盘DIY爱好者Despacito0o&#xff01;今天想和大家分享我开发的机械键盘固件核心头文件HWKeyboard.h的设计思路和技术要点。这个项目是我多年来对键盘固件研究的心血结晶…

2048游戏的技术实现分析-完全Java和Processing版

目录 简介Processing库基础项目构建指南项目结构核心数据结构游戏核心机制图形界面实现性能优化代码详解设计模式分析测试策略总结与展望简介 2048是一款由Gabriele Cirulli开发的经典益智游戏。本文将深入分析其Java实现版本的技术细节。该实现使用了Processing库来创建图形界…

Spring Boot + Elasticsearch + HBase 构建海量数据搜索系统

Spring Boot Elasticsearch HBase 构建海量数据搜索系统 &#x1f4d6; 目录 1. 系统需求分析2. 系统架构设计3. Elasticsearch 与 HBase 集成方案4. Spring Boot 项目实现5. 大规模搜索系统最佳实践 项目概述 本文档提供了基于 Spring Boot、Elasticsearch 和 HBase 构建…

【iOS】YYModel源码解析

YYModel源码解析 文章目录 YYModel源码解析前言YYModel性能优势YYModel简介YYClassInfo解析YYClassIvarInfo && objc_ivarYYClassMethodInfo && objc_methodYYClassPropertyInfo && property_tYYClassInfo && objc_class YYClassInfo的初始化细…

宇树科技更名“股份有限公司”深度解析:机器人企业IPO前奏与资本化路径

从技术落地到资本跃迁&#xff0c;拆解股改背后的上市逻辑与行业启示 核心事件&#xff1a;股改释放的上市信号 2025年5月28日&#xff0c;杭州宇树科技有限公司正式更名“杭州宇树科技股份有限公司”&#xff0c;市场主体类型变更为“股份有限公司”。尽管官方称为常规运营调…

Android Native 内存泄漏检测全解析:从原理到工具的深度实践

引言 Android应用的内存泄漏不仅发生在Java/Kotlin层&#xff0c;Native&#xff08;C/C&#xff09;层的泄漏同样普遍且隐蔽。由于Native内存不受Java虚拟机&#xff08;JVM&#xff09;管理&#xff0c;泄漏的内存无法通过GC自动回收&#xff0c;长期积累会导致应用内存占用…