记一次死锁问题

news/2024/4/25 22:57:20/文章来源:https://blog.csdn.net/qq_37221991/article/details/130370319

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

背景

这个需求要改动一个接口,我这边称为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.luyixian.cn/news_show_103580.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【ROS】ubuntu18.04安装ROS(ROS1 Melodic)

1、添加中科大ROS源 1.1、添加源 sudo sh -c . /etc/lsb-release && echo "deb http://mirrors.ustc.edu.cn/ros/ubuntu/ lsb_release -cs main" > /etc/apt/sources.list.d/ros-latest.list1. 2、添加公钥 sudo apt-key adv --keyserver hkp://keyser…

编译预处理

编译预处理 1、宏定义1.1、 无参宏定义1.2、使用宏定义的优点1.3、宏定义注意点1.4、带参数的宏(重点)1.5、条件编译1.6、宏定义的一些巧妙用法(有用)1.7、结构体占用字节数的计算原则(考题经常考,要会画图)1.8、#在宏定义中的作用&#xff0…

ESP32设备驱动-BMM150数字地磁传感器驱动

BMM150数字地磁传感器驱动 文章目录 BMM150数字地磁传感器驱动1、BMM150介绍2、硬件准备3、软件准备4、驱动实现1、BMM150介绍 BMM150 是一款低功耗、低噪声的 3 轴数字地磁传感器,用于罗盘应用。 具有 1.56 x 1.56 mm 和 0.60 mm 高度的 12 引脚晶圆级芯片级封装 (WLCSP) 为…

直升机空气动力学基础--004翼型的阻力

来源 1. 空气的粘性 2.阻力的产生 3.形成因素 4.阻力系数曲线

转换json格式的日期为Javascript对象的函数

项目中碰到了用jQuery从后台获取的json格式的日期的字符串,需要将此字符串转换成JavaScript的日期对象。 代码如下: //转换json格式的日期(如:{ServerDatetime:"\/Date(1278930470649)\/"})为Javascript的日期对象 fu…

Linux tail 命令

前言 Linux 实时查看日志文件,最主要使用的就是tail命令。 linux tail命令用于显示文件尾部的内容,默认在屏幕上显示指定文件的末尾10行。如果给定的文件不止一个,则在显示的每个文件前面加一个文件名标题。如果没有指定文件或者文件名为“-”…

湿法冶金以及铼提取工艺,湿法冶金工艺特点及工艺流程

湿法冶金是利用浸出剂在一定温度压力下与矿石接触,把矿石中有用的金属溶解后再从溶液中回收有价金属的一种工艺,因为其过程大都是在水溶液中进行,所以又被称为“水法冶金”。 01 湿法冶金工艺特点及工艺流程 湿法冶金作为解决我国金属矿产资…

深度赋能产业数字化转型,蚂蚁集团数字化三件套亮相中国国际金融展

“十四五”规划纲要指出:加快推动数字产业化,推进产业数字化转型,实施“上云用数赋智”行动,推动数据赋能全产业链协同转型。明确提出了通过科技创新,加快产业数字化转型的要求。 4月25日,以“荟萃金融科技…

mybatisPlus·入门·贰

文章目录 1 简单CRUD接口1.1 根据id查询({id传参)1.1.1 接口类直接继承IService1.1.2 controller直接调用方法 1.2 根据ids查询1.3 新增1.3.1 接口类直接继承IService1.3.2 controller直接调用方法 1.4 修改状态(Query传参)1.4.1 …

新版Fluent默认保存的h5文件无法用Tecplot打开的解决办法(亲试有效,评论区是重点)

文章目录 Ansys Fluent简介Fluent 输入/出 文件格式新版Fluent的输出压缩文件(.cas.h5文件)解决办法 Ansys Fluent简介 Ansys Fluent ,是国际上比较流行的商用CFD软件包,在美国的市场占有率为60%,凡是和流体、热传递和…

NetXpert XG2帮您解决“布线安装与维护”难题

在传输大量数据时,光纤变得越来越重要,而铜缆在未来也将继续发挥重要作用,因此我们不仅要比较两种类型布线的优缺点,还要探究光纤传输中的错误来源。 测试光缆传输损耗的准确性对于故障排除至关重要,特别是在光纤情况下…

后台-husky提交代码规范使用

husky是一个git hook工具,可以帮助我们触发git提交的各个阶段:pre-commit、commit-msg、pre-push 1.如何使用husky呢? npx husky-init && npm installWindows安装不成功试试npx husky-init && npm install 2.git commit规范…

Java学习笔记-01

目录 jdk安装及环境配置 java前置知识 编写一个HelloWorld 常量 数据类型 变量 类型转换 算数运算符 一元运算符( --) 关系运算符 逻辑运算符(与或非,逻辑异或) 三元(三目)运算符 Scanner类的简单使用 jdk安装及环境配置 看到的一篇文章,…

【C++STL】set

前言 前面的CSTL的博客,我们介绍了string,vector,list,deque,priority_queue还有stack和queue。 这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。 而从本节开…

(二)ElasticSearch 辅助工具 Kibana 介绍与安装

1、什么是 kibana ? Kibana 是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索引中的数据。使用Kibana,可以通过各种图表进行高级数据分析及展示。 Kibana让海量数据更容易理解。它操作简单&#xff…

0401概述-最短路径-加权有向图-数据结构和算法(Java)

文章目录 1 最短路径2 最短路径的性质3 加权有向图的数据结构3.1 加权有向边3.2 加权有向图 4 最短路径4.1 最短路径API4.2 最短路径的数据结构4.3 边的松弛4.4 顶点的松弛 结语 1 最短路径 如图1-1所示,一幅加权有向图和其中的一条最短路径: 定义&…

加强人工智能共性技术研发与产业化协同发展

央视网消息:“以5G为代表的新一代信息技术与制造业、交通、旅游等实体经济重要领域深度融合。”4月20日下午,国新办举行一季度工业和信息化发展情况新闻发布会,相关部门负责人在答问时表示,将用好融合应用这把金钥匙,开…

基于matlab仿真相控天线阵列在波束成形MIMO-OFDM系统中的使用

一、前言 本例显示了相控阵在采用波束成形的MIMO-OFDM通信系统中的使用。它使用通信工具箱和相控阵系统工具箱中的组件,对组成发射器和前端接收器组件的辐射元件进行建模,用于MIMO-OFDM通信系统。使用用户指定的参数,您可以根据不同空间位置和…

JAVA Future类详解及Thread线程是如何运行Future类的

一、Future基本介绍 Future(java.util.concurrent Interface Future<V>)表示异步计算的结果。Future接口提供了检查计算是否完成、检查计算是否被取消、等待计算完成并获取计算结果等方法。 在并发编程中&#xff0c;我们经常用到非阻塞的模型&#xff0c;但继承thread类…

202303-1 田地丈量

代码 #include<iostream> #include<vector> #include<string> #include<cmath> #include<algorithm> #include<stack> using namespace std; int n, a, b;int main() {cin >> n >> a >> b;int x1, y1, x2, y2;int x, y;…