spring 事务不提交_在Spring事务管理下,Synchronized为啥还线程不安全?

作者: 六脉神剑

来源:juejin.im/post/5ddc7a23e51d452331202721

在synchronized 锁住方法的情况下,竟然出现了脏写

Tips

昨天本来打算是准备着一支烟 一杯咖啡 一个bug写一天的,突然我们组长跟我们说线上环境报错了,还出现了"服务器异常,请联系管理员"

这特么不是一级事故吗?虽然有测试在前面扛枪。但是是我负责的直播模块,心理慌的一批(ps 报错图当时没保存了)

分析事故原因

因为是报错(因为我做这条数据查询的时候是selectOne 所以会报出现了sql异常) 原因到是很快找到了 数据库出现了脏写如图:

1b39d6c9ad334f9a7d83a20728393628.png

我负责的是直播模块 其中的一个业务是直播结束后第三方会通知我去拉取直播的回放,但是这个回放有可能一条,也有可能是多条,但是我们的业务要求是只需要保存一条直播回放所以我这会做如下操作:

9b8a64948b832b62587e6ddb5f6538e8.png

我在做插入之前我会做一个校验,并且我还加了一个方法级别的锁 并且线上我们只有一个副本,竟然还出现了脏写 我的fuck,我这是见了鬼了吧

解决问题的过程

我怀着百思不得其解的心理打算去找答案

首先我模拟了一个并发环境:

54afc59a863f67365ccf2ab78f5c744f.png

通过CountDownLatch 去模拟并发看看数据是否会有问题:结果测试线的数据如下:

d4b67798c89f1e1eb84d45df4e60d6f4.png

我去还真出现了 而且是一部分出现脏写,一部分没有成功,我特么 fuck 心理一万次想说这特么我怎么找

测了十来次 然后觉得肯定是有问题的 然后冷静下来 因为我打了日志 发现2个线程确实是顺序执行的(这里的截图就没有贴了)

众所周知,synchronized方法能够保证所修饰的代码块、方法保证有序性、原子性、可见性。

那么这说明什么呢 我一想肯定Synchronized 它是起到它的作用的 一个线程执行完成之后,另外一个线程再来执行,突然灵光一闪 是不是下一个线程再做幂等校验的时候 读到了上一次还没有提交的事务 所以造成了脏读,脏写的原因呢

然后我把在类上的 @Transactional 注解去掉

d84311eeea29e971e32cb2b985c630be.png

果然后面测了几次 再也没出现上面的情况了

Tips 特别感谢一位不愿透露姓名的大佬的指出说我没有把标题的内容说清楚和后面的解决问题的收场的时候有点草率

在这里 我再好好的说一下我标题是 在Spring事务管理下,Synchronized为啥还线程不安全?

其实又是自己并没有用Synchronized 锁住 Spring 的事务

因为我的列子上的@Transaction注解是再类上面(也就是再方法上面)Spring的声明事事务他是利用了aop的思想

我虽然锁住了第一个线程 但是等到第一个线程的事务 还没提交的时候,第二个线程就去查询了 所以就会导致线程不安全问题

解决问题

方案1 很简单 那就是不开事务就行了,再这个方法上不加事务就行 因为 Synchronized 可以保证线程安全。

这个方案的意思就是说不要再同一个方法上用@Transaction 和 Synchronized 例子图就没有贴了 就像我前面的 把注解去掉就好了 (但是前提你这个方案确定是不需要事务)

方案2 在这个里面再调用一层service 让那个方法提交事务,这样的话加上Synchronized 也能保证线程安全。

方案2我贴下代码吧

838220ad43ca5774498beca88befbdd3.png

其实也就是说把事务包裹在Synchronized 里面

利用中午的时间测了几次 确实是不会出现线程安全问题了

方案3 用redis 分布式锁 也是可以的 就算是多个副本也是能保证线程安全。这个后面的文章会有写到

结论

在多线程环境下,就可能会出现:方法执行完了(synchronized代码块执行完了),事务还没提交,别的线程可以进入被synchronized修饰的方法,再读取的时候,读到的是还没提交事务的数据,这个数据不是最新的,所以就出现了这个问题。

参考了一位读者的结论

Synchronized 失效关键原因:是因为Synchronized锁定的是当前调用方法对象,而Spring AOP 处理事务会进行生成一个代理对象,并在代理对象执行方法前的事务开启,方法执行完的事务提交,所以说,事务的开启和提交并不是在 Synchronized 锁定的范围内。

出现同步锁失效的原因是:当A(线程) 执行完insertSelective()方法,会进行释放同步锁,去做提交事务,但在A(线程)还没有提交完事务之前,B(线程)进行执行findOrder() 方法,执行完毕之后和A(线程)一起提交事务, 这时候就会出现线程安全问题。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值