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

记一次死锁问题

最近在做一个需求,碰到了死锁的问题,记录下解决问题的过程

背景

这个需求要改动一个接口,我这边称为A接口,原先的逻辑是A接口内部会调用c方法,c方法是一个dubbo方法,
现在需要再A接口里添加调用B方法,b方法是本地调用。

A接口的入参是某个商品的编码,拿到这个商品编码后匹配到一个交易订单,B方法和C方法都是需要操作这个订单商品的库存,所以都上了锁。

原逻辑

在这里插入图片描述

现逻辑

在这里插入图片描述

加完B方法后,发现每次调用接口都会超时,分析日志发现是c方法加库存锁超时了。c方法的库存锁还未释放,同时B方法又加了这个锁,B需要等待C占用的锁释放,而c的锁释放也需要等待B方法结束,于是就产生了死锁

于是我产生了以下几个疑问

1、我们的锁不是支持可重入吗,为什么还会死锁?

是因为可重入是需要同一个线程,而c方法因为是dubbo调用,已经不是一个同一个线程了,就会产生互斥的效果

2、我们项目中的锁做了统一处理,都是在事务结束后才释放锁,是否可以在B方法执行完就释放锁?

这个地方我们之前是踩过坑的,如果在事务完成前释放锁,那么另一个线程会拿到锁,但是因为前面的事务还未提交,所以他查询的数据还是老数据。这样加锁就没有达到目的了,所以必须在事务完成后再释放锁

解决方法

方案1

首先我的想法是能否从业务上解决,如果业务上不关心B方法跟A方法的一致性的一致性的话,就是如果A方法报错了,B方法不需要回滚,那么最简单的做法就是B方法新起一个事务,这样当B方法结束后,B的事务也就结束了,库存锁也就释放了,也就不会影响到c方法

但是产品的想法是需要保证一致,所以这个方法不能搞

方案2

将B方法的加锁和c方法的加锁挪到最外层A方法上,B方法和c方法都不用加锁了

这种方案可以解决问题,但是会引发其他问题,一个就是加锁的范围变大了,会影响这个接口整体的性能,其次需要改动B方法和c方法,也容易改出问题

方案3

B方法和c方法顺序调换,先执行B方法再执行C方法,因为B方法是dubbo方法,所以B方法执行完后它自己的事务也就结束了,B占用的库存锁也会释放。再执行c方法就不会导致锁等待

这个方案是可以实施的,但是会出现一种情况,当c方法报错时,B方法因为已经执行完了,就无法回滚了。之前B方法调用放在最后调用是不会有问题的。同时B方法和c方法顺序调换需要调整一些代码,不是简单的调换就可以的,基于这两点我最后也没有采用这个方案

方案4

结合方案1,B方法另起一个事务,同时如果后续逻辑报错了,捕获异常,调用B方法的回滚方法(也是新事务)
有点像tcc模式

最终采用了这个方案,改动量和影响点最小

碰到了另一个坑

另起一个事务用了Propagation.REQUIRES_NEW 这个传播级别,但是测试的时候发现还是死锁,似乎没有起到效果,于是我回想起事务失效的几种场景,又复习了遍AOP的底层原理,知道自己踩坑了

原始代码A方法调用B方法

service A {@Transactionalmethod A() {B();}@Transactional(propagation = Propagation.REQUIRES_NEW)method B() {}
}

spring启动后会扫描@Transactional注解,生成代理类AProxy,添加事务的逻辑

service AProxy {A a;method A() {// 开启事务a.B();// 结束事务}// 没有走这个方法method B() {// 新开事务// 结束事务}
}

当我们执行A方法,会执行代理类AProxy的方法,但是调用B方法是没有调用代理类的B方法,而是service A自己原本的方法,所以method B上的注解没有其效果

解决方法也有好几种,我这边是将method B方法写在其他类中,跨类调用就可以了

http://www.lryc.cn/news/61769.html

相关文章:

  • Bean 作⽤域和⽣命周期
  • SVN通过备份、过滤、再导入的方式彻底删除废弃目录
  • golang支持优雅关闭和core错误记录
  • Basics of Container Isolation 容器隔离的实现原理
  • EBS R12.1 注册客户化应用的步骤
  • 算法记录 | Day38 动态规划
  • PMP项目管理-[第六章]进度管理
  • Python变量
  • 准备换工作的看过来~
  • 免费AI人工智能在线写作伪原创-百度ai自动写文章
  • 互联网摸鱼日报(2023-04-21)
  • 5.3、web服务器简介HTTP协议
  • 【观察】华为:新一代楼宇网络,使能绿建智慧化
  • 【C# .NET】chapter 13 使用多任务改进性能和可扩展性
  • CA(证书颁发机构)
  • 辛弃疾最有代表性的十首词
  • MC9S12G128开发板—实现按键发送CAN报文指示小车移动功能
  • 尚融宝22-提交借款申请
  • 机器学习在生态、环境经济学中的实践技术应用及论文写作
  • Android硬件通信之 WIFI通信
  • 面试官:“请描述一下Android系统的启动流程”
  • k8s delete node 后 重启kubelet会自己加入到集群 ?
  • REXROTH液压方向阀安装须知
  • 【数据结构实验】哈夫曼树
  • 浏览器不好用?插件来帮忙
  • Qt Quick - 容器控件综述
  • 面试题30天打卡-day06
  • Spring Boot的基础使用和< artifactId>spring-boot-maven-plugin</ artifactId>爆红的处理
  • 项目管理中的必不可少的强大工具有哪些?
  • 嵌入式学习笔记——SPI通信的应用