演示synchronized锁机制用法的简单Demo
演示synchronized
锁机制用法的简单Demo。我们以"银行开户"场景为例:每个用户只能创建一个账户(模拟类似原代码中每个用户只能有一个私有空间的限制)。
第1步:创建项目结构
demo-lock
├── src/main/java/com/example/demo/
│ ├── controller/AccountController.java
│ ├── entity/Account.java
│ ├── mapper/AccountMapper.java
│ ├── service/AccountService.java
│ └── DemoApplication.java
└── src/main/resources/└── application.yml
第2步:添加依赖(pom.xml)
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>
第3步:实体类 Account.java
@Data
@TableName("t_account")
public class Account {@TableId(type = IdType.AUTO)private Long id;private Long userId;private BigDecimal balance;
}
第4步:Mapper接口 AccountMapper.java
public interface AccountMapper extends BaseMapper<Account> {
}
第5步:Service层 AccountService.java
@Service
@RequiredArgsConstructor
public class AccountService {private final AccountMapper accountMapper;// 无锁版本(存在并发问题)public void createAccountUnsafe(Long userId) {Long count = accountMapper.selectCount(new QueryWrapper<Account>().eq("user_id", userId));if (count > 0) {throw new RuntimeException("用户已存在账户");}Account account = new Account();account.setUserId(userId);account.setBalance(BigDecimal.ZERO);accountMapper.insert(account);}// 有锁版本(线程安全)public void createAccountWithLock(Long userId) {String lockKey = String.valueOf(userId).intern();synchronized (lockKey) {createAccountUnsafe(userId);}}
}
第6步:Controller层 AccountController.java
@RestController
@RequiredArgsConstructor
public class AccountController {private final AccountService accountService;// 不安全的开户接口(用于演示并发问题)@GetMapping("/unsafe/{userId}")public String unsafeCreate(@PathVariable Long userId) {try {accountService.createAccountUnsafe(userId);return "开户成功";} catch (Exception e) {return e.getMessage();}}// 安全的开户接口(使用synchronized锁)@GetMapping("/safe/{userId}")public String safeCreate(@PathVariable Long userId) {try {accountService.createAccountWithLock(userId);return "开户成功";} catch (Exception e) {return e.getMessage();}}
}
第7步:配置文件 application.yml
spring:datasource:url: jdbc:mysql://localhost:3306/demo?useSSL=false&characterEncoding=utf8username: rootpassword: your_passwordmybatis-plus:configuration:map-underscore-to-camel-case: true
第8步:测试步骤
- 初始化数据库
CREATE DATABASE IF NOT EXISTS demo;
USE demo;CREATE TABLE t_account (id BIGINT PRIMARY KEY AUTO_INCREMENT,user_id BIGINT NOT NULL UNIQUE,balance DECIMAL(10,2) NOT NULL DEFAULT 0
);
ALTER TABLE t_account DROP INDEX user_id;
- 启动应用
mvn spring-boot:run
- 并发测试(使用JMeter或Postman)
测试不安全接口:
- 用多个线程同时调用
GET http://localhost:8080/unsafe/123
- 可能结果:成功创建多个账户(违反唯一约束)
测试安全接口:
- 用多个线程同时调用
GET http://localhost:8080/safe/456
- 结果:只会有第一个请求成功创建账户
关键代码解释
- 锁对象的选择:
String lockKey = String.valueOf(userId).intern();
intern()
保证相同userId值返回同一个String对象(来自字符串常量池)- 不同userId对应的锁对象不同,实现细粒度锁
- 同步代码块:
synchronized (lockKey) {// 临界区代码
}
- 确保同一用户的并发请求串行执行
- 不同用户的请求可以并行处理
典型输出对比
无锁接口测试结果:
第一次请求:开户成功(创建账户)
第二次请求:Duplicate entry '123' for key 'user_id'(违反唯一约束)
有锁接口测试结果:
第一次请求:开户成功(创建账户)
后续所有请求:用户已存在账户(业务校验拦截)
总结说明表格
关键点 | 说明 |
---|---|
synchronized范围 | 基于用户ID的细粒度锁,不影响其他用户操作 |
String.intern() | 保证相同userid得到的String是同一个对象(来自字符串常量池) |
事务边界 | 在锁范围内包含整个事务操作(确保查询和插入操作的原子性) |
性能影响 | 只对相同用户的并发请求串行化处理,不影响不同用户的并发处理 |
适用场景 | 需要基于特定维度(如用户ID)进行并发控制的场景 |
可以通过这个Demo逐步体验:
- 先观察不加锁时的并发问题
- 再体验加锁后的线程安全效果
- 最后尝试调整userId观察不同用户的并发情况
Jmeter测试
-
设置 HTTP 请求
注意:这里让多个线程同时使用相同的 userId。/unsafe/1
,这样所有线程都会尝试为同一个用户(userId=1)创建账户。 -
设置线程组
-
添加 查看结果树
-
运行
-
数据库结果
-
加了锁的
无论 并发请求是多少,在关闭数据库的唯一约束的情况下,数据库插入条数始终为1。
-
没有加锁
产生了多条插入记录,显然是不合理的。
相关文章:

演示synchronized锁机制用法的简单Demo
演示synchronized锁机制用法的简单Demo。我们以"银行开户"场景为例:每个用户只能创建一个账户(模拟类似原代码中每个用户只能有一个私有空间的限制)。 第1步:创建项目结构 demo-lock ├── src/main/java/com/exampl…...

Datawhale 数学建模导论二 笔记1
第6章 数据处理与拟合模型 本章主要涉及到的知识点有: 数据与大数据Python数据预处理常见的统计分析模型随机过程与随机模拟数据可视化 本章内容涉及到基础的概率论与数理统计理论,如果对这部分内容不熟悉,可以参考相关概率论与数理统计的…...
差分解方程
差分解方程 差分法在数值求解偏微分方程(PDEs)和常微分方程(ODEs)时,可以分为隐式格式和显式格式。以下是两者的主要区别: 显式格式(Explicit Scheme) 时间推进: 显式格…...

EasyExcel 复杂填充
EasyExcel Excel表格中用{}或者{.} 来表示包裹要填充的变量,如果单元格文本中本来就有{、}左右大括号,需要在括号前面使用斜杠转义\{ 、\}。 代码中被填充数据的实体对象的成员变量名或被填充map集合的key需要和Excel中被{}包裹的变量名称一致。 …...
ESP32通过MQTT连接阿里云平台实现消息发布与订阅
文章目录 前言 一、准备工作 二、阿里云平台配置 三、代码实现 总结 前言 本文将介绍如何使用ESP32开发板通过MQTT协议连接阿里云物联网平台,并实现消息的发布与订阅功能。我们将使用Arduino IDE进行开发,并借助PubSubClient库实现MQTT通信。 一、准备…...

NVIDIA Jetson Orin Nano 刷机过程
1. 背景 新到手 NVIDIA Jetson Orin Nano 插上显示屏,显示如下: 这是UEFI Shell,UEFI Shell(统一可扩展固件接口外壳程序)是一种基于UEFI规范的交互式命令行工具,它运行在UEFI固件环境中,为用…...
C#学习之数据转换
目录 一、创作说明 二、数据类型之间的转换 1.数据类型之间的转换表格 2.代码示例 三、进制之间的转换 1.进制之间的转换表格 2.代码示例 四、ASCII 编码和字符之间的转换 1.ASCII 编码和字符之间的转换表格 2.代码示例 五、总结 一、创作说明 C#大多数时候都是和各…...

typecho快速发布文章
typecho_Pytools typecho_Pytools工具由python编写,可以快速批量的在本地发布文章,不需要登陆后台粘贴md文件内容,同时此工具还能查看最新的评论消息。… 开源地址: GitHub Gitee 使用教学:B站 一、主要功能 所有操作不用登陆博…...

深度学习R4周:LSTM-火灾温度预测
🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊 任务: 数据集中提供了火灾温度(Tem1)、一氧化碳浓度(CO 1)烟雾浓度(Soot 1)…...
探索Java中的集合类_特性与使用场景
1. 引言 1.1 Java集合框架概述 Java集合框架(Java Collections Framework, JCF)是Java中用于存储和操作一组对象的类和接口的统称。它提供了多种数据结构来满足不同的需求,如列表、集合、映射等。JCF的核心接口包括Collection、List、Set、Queue和Map,以及它们的各种实现…...

自动化遇到的问题记录(遇到问题就更)
总结回归下自己这边遇到的一些问题 “EOF错误”,获取不到csv里面的内容 跑多csv文件里的场景,部分场景的请求值为 1、检查csv文件里不能直接是[]开头的参数,把[]改到ms平台的请求参数里 2、有时可能是某个参数值缺了双引号的其中一边 met…...

【云安全】云原生- K8S kubeconfig 文件泄露
什么是 kubeconfig 文件? kubeconfig 文件是 Kubernetes 的配置文件,用于存储集群的访问凭证、API Server 的地址和认证信息,允许用户和 kubectl 等工具与 Kubernetes 集群进行交互。它通常包含多个集群的配置,支持通过上下文&am…...
【愚公系列】《Python网络爬虫从入门到精通》008-正则表达式基础
标题详情作者简介愚公搬代码头衔华为云特约编辑,华为云云享专家,华为开发者专家,华为产品云测专家,CSDN博客专家,CSDN商业化专家,阿里云专家博主,阿里云签约作者,腾讯云优秀博主,腾讯云内容共创官,掘金优秀博主,亚马逊技领云博主,51CTO博客专家等。近期荣誉2022年度…...

【Linux】Ext2文件系统、软硬链接
Ext2文件系统 一.理解硬件1.磁盘、服务器、机柜、机房2.磁盘的物理结构3.磁盘的存储结构4.磁盘的逻辑结构1.理解过程2.真实过程 5.CHS地址、LBA地址转换 二.引入文件系统1.引入"块"概念2.引入"分区"概念3.引入"inode"概念 三.Ext2文件系统1.宏观…...

ATF系统安全从入门到精通
CSDN学院课程连接:https://edu.csdn.net/course/detail/39573...

【算法专场】哈希表
目录 前言 哈希表 1. 两数之和 - 力扣(LeetCode) 算法分析 算法代码 面试题 01.02. 判定是否互为字符重排 编辑算法分析 算法代码 217. 存在重复元素 算法分析 算法代码 219. 存在重复元素 II 算法分析 算法代码 解法二 算法代码 算法…...

Beszel监控Docker安装
一、Beszel Hub安装 #Beszel Hub安装 mkdir -p ./beszel_data && \ docker run -d \--name beszel \--restartunless-stopped \-v ./beszel_data:/beszel_data \-p 8090:8090 \henrygd/beszel#创建账号 账号/密码:adminadmin.com/adminadmin.com 二、Besz…...
如何学习Elasticsearch(ES):从入门到精通的完整指南
如何学习Elasticsearch(ES):从入门到精通的完整指南 嘿,小伙伴们!如果你对大数据搜索和分析感兴趣,并且想要掌握Elasticsearch这一强大的分布式搜索引擎,那么你来对地方了!本文将为…...
【mybatis】基本操作:详解Spring通过注解和XML的方式来操作mybatis
mybatis 的常用配置 配置数据库连接 #驱动类名称 spring.datasource.driver-class-namecom.mysql.cj.jdbc.Driver #数据库连接的url spring.datasource.urljdbc:mysql://127.0.0.1:3306/mybatis_test characterEncodingutf8&useSSLfalse #连接数据库的名 spring.datasourc…...

CSV格式和普通EXCEL格式文件的区别
CSV 文件(.csv) 普通的 Excel 文件(.xlsx 或 .xls) 主要体现在 文件格式、数据存储、功能支持 等方面: 文件格式 比较项CSV 文件 (.csv)Excel 文件 (.xlsx/.xls)文件类型纯文本文件二进制或 XML 格式数据分隔逗号(,)…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...

学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...

2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
Xen Server服务器释放磁盘空间
disk.sh #!/bin/bashcd /run/sr-mount/e54f0646-ae11-0457-b64f-eba4673b824c # 全部虚拟机物理磁盘文件存储 a$(ls -l | awk {print $NF} | cut -d. -f1) # 使用中的虚拟机物理磁盘文件 b$(xe vm-disk-list --multiple | grep uuid | awk {print $NF})printf "%s\n"…...

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...

Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...

TSN交换机正在重构工业网络,PROFINET和EtherCAT会被取代吗?
在工业自动化持续演进的今天,通信网络的角色正变得愈发关键。 2025年6月6日,为期三天的华南国际工业博览会在深圳国际会展中心(宝安)圆满落幕。作为国内工业通信领域的技术型企业,光路科技(Fiberroad&…...

【C++】纯虚函数类外可以写实现吗?
1. 答案 先说答案,可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...