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

多线程下 到底是事务内部开启锁 还是先加锁再开启事务?

前言

不知大家是否有观察到一个最常见的错误:

先开启事务,然后针对资源加锁,操作资源,然后释放锁,最后提交事务

你是否发现了在这样的场景下会出现并发安全的问题?
(提示:一个线程A在事务内部释放锁,另一个线程B拿到了锁,线程B看不到线程A的操作 导致 线程B 重复执行线程A已经对资源进行的操作)

用一个业务场景去说明

用一个电商系统的订单处理场景来具体说明这个“事务没有完全被锁包住”会导致的问题。

需求: 用户点击“下单”按钮后,后台要:

检查该用户是否已经提交过该订单(防重复下单)

如果没有,就创建订单

并扣减库存

错误的业务代码:

@Transactional
public void createOrder(String userId, String orderNo) {if (!orderService.hasOrdered(orderNo)) {synchronized (("lock:" + orderNo).intern()) {// 锁内逻辑// 此处加锁}// 锁释放了,但事务还没提交orderService.save(orderNo);  // 保存订单productService.decreaseStock(); // 扣减库存}
}

1、线程A

开启事务

查询:是否已下单?→ 查询不到(因为数据库未提交)

执行下单逻辑:准备插入订单

!! 锁在事务内部,提前释放

事务还没提交!

2、线程B

紧接着执行相同操作

开启事务

查询:是否已下单?→ 同样查询不到(线程A没提交)

执行下单逻辑:插入重复订单、扣减库存

提交事务

最终结果

因为 数据库在**“读已提交**”隔离级别下,线程B看不到线程A未提交的插入

又因为加锁只包了业务逻辑而不是整个事务范围

所以锁一旦提前释放,线程B就能并发进来了
线程A和线程B都成功下了单

结果就是 重复支付 / 重复下单

改进

public void createOrderSafe(String userId, String orderNo) {synchronized (("lock:" + orderNo).intern()) { // 线程B被阻塞doCreateOrder(userId, orderNo);           // 锁保护整个事务 内部是本地事务}
}

或者微服务中 使用redis分布式锁

RLock lock = redissonClient.getLock("order:" + orderNo);
if (lock.tryLock()) {try {// 事务中执行订单判断与插入} finally {lock.unlock(); // 锁直到事务结束才释放}
}

总结

锁放在事务外部

相关文章:

  • AnimateCC及CreateJS:打飞机的4版本V1、V2、V3、V4
  • hot100 -- 12.栈系列
  • 金蝶云星空BOS开发
  • Linux进程间通信(IPC)详解:从入门到理解
  • 深度分析Javascript中的Promise
  • 人工智能学习19-Pandas-设置
  • 当遇到“提交失败:404”的问题时,通常表明前端请求的URL无法正确匹配到后端的Servlet或资源。
  • aflplusplus:开源的模糊测试工具!全参数详细教程!Kali Linux教程!(四)
  • 【办公类-25-05】20250514 Python模拟UIBOT上传园园通截图(自动最小化界面,时间部分的删除和黏贴)
  • SCADA|KingSCADA对比显示任意几条实时曲线的方法
  • 理论物理学中的规范场论与全息原理
  • Go语言底层(五): 深入浅出Go语言的ants协程池
  • C#最佳实践:推荐使用泛型而非装箱、拆箱
  • 华为云Flexus+DeepSeek征文 | 基于Dify构建股票分析助手
  • 语音交互革命:基于 Amazon Nova Sonic + MCP 构建下一代沉浸式 Agent
  • LLMs 系列实操科普(6)
  • 嵌入式学习
  • 德国马克斯·普朗克数学研究所:几何朗兰兹猜想
  • JS进阶 Day03
  • 深度理解 CLIP:连接图像与语言的桥梁
  • 织梦cms怎样做网站/自动点击器免费下载
  • 5星做号宿水软件的网站/如何推广小程序平台
  • 怎么做网站写手/百度指数如何分析数据
  • 专业建站服务公司/全网引流推广 价格
  • 网站建设需求调研/免费发帖平台
  • 整站优化外包公司/百度售后客服电话24小时