当Transactional碰到锁,有个大坑,要小心。 (中)

简介: 当Transactional碰到锁,有个大坑,要小心。 (中)

诶,你看这个调用栈,我框起来的这个地方:


image.png


看这个名字,你就不好奇吗?

它简直就是在跳着脚,在喊你:点我,快,愣着干啥,你TM快点我啊。我这里有秘密!

然后,我就这样轻轻的一点,就到了这里:

org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction

这里有个切面,可以理解为 try 里面就是在执行我们的业务代码逻辑:


image.png

而在 try 代码块,执行我们的业务代码之前,有这样的一行代码

image.png

找到这里了,你就在这一行代码之前,再轻轻的打个断点,然后调试进去,就能找到这一小节开始的时候,说的这个方法:

org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin

不信?你看嘛,我不骗你。

它们之间只隔了三个调用:

image.png


这样就找到答案了。

调用栈,另一个调试源码小技巧,屡试不爽,送给你。


之前还是之后


好了,前面是开胃菜,可能有的同学吃开胃菜就已经弄饱了。

没事,现在上正餐,再按一按还是能吃进去的。

image.png

还是拿前面的这份代码来说事,流程就是这样的:

image.png

  • 1.先拿锁。
  • 2.查询库存。
  • 3.判断是否还有库存。
  • 4.有库存则执行减库存,创建订单的逻辑。
  • 5.没有库存则返回。
  • 6.释放锁。

所以代码是这样的

image.png

完全符合我们之前的那份代码片段,有事务,也有锁:

image.png

回到我们最开始抛出来的问题:

在上面的示例代码的情况下那么事务的提交到底是在 unlock 之前还是之后呢?

我们可以带入一个具体的场景。

比如我数据库里面有 10 个顶配版的 iPad,原价 1.6w 元一台,现在单价 1w 一个,这个价格够秒杀吧?

微信图片_20220428160230.jpg


反正一共就 10 台,所以,我的数据库里面是这样的,

image.png

然后我搞 100 个人来抢东西,不过分吧?

我这里用 CountDownLatch 来模拟一下并发:

image.png

执行一下,先看结果,立马就见分晓:

image.png

动图右边的部分:

上面是浏览器请求,触发 Controller 的代码。

然后中间是产品表,有 10 个库存。

最下面是订单表,没有一条数据。

触发了代码之后,库存为 0 了,没有问题。

但是,订单居然有 20 笔!

也就是说超卖了 10 个ipad pro 顶配版!

超卖的,可不在活动预算范围内啊!

那可就是一个 1.6w 啊,10 个就是 16w 啊。

就这么其貌不扬,人畜无害,甚至看起来猥猥琐琐的代码,居然让我亏了整整 16w 。

image.png

其实,结果出现了,答案也就随之而来了。

在上面的示例代码的情况下,事务的提交在 unlock 之后。

image.png

其实你仔细分析后,猜也能猜出来,肯定是在 unlock 之后的。

而且上面的描述“unlock之后”其实是有一定的迷惑性的,因为释放锁是一个比较特别的操作。

换一个描述,就比较好理解了:

在上面的示例代码的情况下,事务的提交在方法运行结束之后。

你细品,这个描述是不是迷惑性就没有那么强了,甚至你还会恍然大悟:这不是常识吗?

image.png

为什么是方法结束之后,分析具体原因之前,我想先简单分析一下这样的代码写出来的原因。

我猜可能是这样的。

最开始的代码结构是这样:

image.png

然后,写着写着发现不对,并发的场景下,库存是一个共享的资源,这玩意得加锁啊。

于是搞了这出:


image.png


后面再次审查代码的时候,发现:哟,这个第三步得是一个事务操作才行呀。

于是代码就成了这样:


image.png


演进路线非常合理,最终的代码看起来也简直毫无破绽。

但是问题到底出在哪里了呢?


image.png

目录
相关文章
|
8月前
|
Java 编译器 数据库
@Transactional中指定rollbackFor,弊端以及不能回滚的时候
@Transactional中指定rollbackFor,弊端以及不能回滚的时候
316 3
|
5月前
|
小程序 Java 开发工具
【Java】@Transactional事务套着ReentrantLock锁,锁竟然失效超卖了
本文通过一个生动的例子,探讨了Java中加锁仍可能出现超卖问题的原因及解决方案。作者“JavaDog程序狗”通过模拟空调租赁场景,详细解析了超卖现象及其背后的多线程并发问题。文章介绍了四种解决超卖的方法:乐观锁、悲观锁、分布式锁以及代码级锁,并重点讨论了ReentrantLock的使用。此外,还分析了事务套锁失效的原因及解决办法,强调了事务边界的重要性。
154 2
【Java】@Transactional事务套着ReentrantLock锁,锁竟然失效超卖了
|
7月前
|
存储 Java 关系型数据库
Spring事务失效的 8 大原因,这次可以吊打面试官了!
Spring事务失效的 8 大原因,这次可以吊打面试官了!
|
Java 数据库连接 API
十二.Spring源码剖析-Transactional 事务执行流程
上一篇《[Transactional源码解析](https://blog.csdn.net/u014494148/article/details/118398677)》我们介绍了Spring对Transactional的解析,也就是事务的初始化工作,这一篇我们接着来分析事务的执行流程。
|
Java 测试技术 数据库
【事务与锁】当Transactional遇上synchronized
最近工作中遇到某些七七八八的问题,就是与事务和锁、并发都有着紧密联系相关的问题所在。主要情况是:通过调用方法获取编号,而这个编号是递增有序的,并且存在于数据库中,简单理解就是需要用到这种编号(以下称任务编号),需要从数据库获取出来,在+1最为本次需要的编号,然后在存回数据库中,提供下次使用。
959 0
【事务与锁】当Transactional遇上synchronized
|
消息中间件 JavaScript 小程序
如何将 @Transactional 事务注解运用到炉火纯青?
如何将 @Transactional 事务注解运用到炉火纯青?
|
SQL 消息中间件 JavaScript
我在项目里用@Transactional注解控制事务,结果完全不生效,纳尼?
我在项目里用@Transactional注解控制事务,结果完全不生效,纳尼?
|
缓存 Java 数据库连接
Spring源码剖析之Transactional事务
我们知道使用@Transactional,要满足以下条件 1、配置数据源 DataSource 2、配置事务管理器 PlatformTransactionManager 3、配置类上标识 @EnableTransactionManagement
192 0
|
存储 SQL Java
聊一聊使用事务时(@Transactional)可能出现的问题
聊一聊使用事务时(@Transactional)可能出现的问题
342 0
聊一聊使用事务时(@Transactional)可能出现的问题
|
Java 关系型数据库 MySQL
面试突击83:什么情况会导致@Transactional事务失效?
面试突击83:什么情况会导致@Transactional事务失效?
183 0

热门文章

最新文章