事务代码中加synchronized锁引发的bug

news/2025/1/15 22:54:04/

背景

最近解决了个BUG,由于历史背景,在某一个产品里的用户中心有两套系统,两套系统还使用了两个不同的数据库,所以创建用户的时候会有一个新数据库到旧数据库同步的操作。

具体的流程是用户在页面注册了新用户,请求被新用户中心系统a处理,然后通过消息组件同步到用户中心系统b中,用户只要修改了用户的信息不论是手机号、年龄、姓名等等都会异步触发同步机制,一切听起来都很不合理中透露着合理。

BUG从现象上看是出现了一个用户在旧数据库中出现多条的情况。最烦查这种没有具体堆栈报错的bug,因为搞不好得捋一遍祖传的屎山代码。。。

代码分析

出现这种情况肯定是在接受消息的方法那边处理,因为消息组件难免出现重复发送的情况,一般都会在消费端做幂等处理。

@Transactional
public void consumer(String msg) {UserDTO userdto = JsonUtil.toObject(msg, UserDTO.class);// 一大堆屎山业务逻辑。。。。。synchronized(this) {//操作数据库读写usercenter.save()}
}

这里乍一看感觉没啥问题,多看两眼觉得有点奇怪怪的又说不上哪里怪,其实问题就出在了这里。要么说synchronized关键字要慎重使用,

理性分析一波,@Transactional是通过AOP的方式实现对数据库的事务管理,而synchronized代码块又是在一个事务内,就会出现第一个线程释放锁后但是事务还没来得及提交,第二个线程就进入同步代码块获取到未提交的数据库数据。有点类似脏读。

那怎么解决呢

解决方案

这好办,我直接套个方法不就得了,把锁提到方法外面

public synchronized consumer(String msg) {consumer(msg);
}@Transactional
public void consumer(String msg) {UserDTO userdto = JsonUtil.toObject(msg, UserDTO.class);// 一些业务判断逻辑。。。。。//操作数据库读写usercenter.save()
}

这样又会出现另一个问题,你会发现事务失效了,同类内方法互调使用不了AOP包装后的事务方法。看起来一个很好改的小BUG,改起来还得细心点,不然解决一个旧BUG出来新BUG。

这里有三个思路吧,大家有好思路也可以提一下:

  1. 拆成两个类,保存用户一个类,synchronized方法放到另一个类
  2. 自己注入自己,使用AOP包装后的对象调用consumer方法
  3. 手动管理事务

这种老功能最怕乱改代码,所以尽量选择改动量最少的方法,最终使用第二种方法解决

@Autowire
UserServiceImpl userServiceImpl;public synchronized consumerProxy(String msg) {userServiceImpl.consumer(msg);
}@Transactional
public void consumer(String msg) {UserDTO userdto = JsonUtil.toObject(msg, UserDTO.class);// 一些业务判断逻辑。。。。。//操作数据库读写usercenter.save()
}

http://www.ppmy.cn/news/1521335.html

相关文章

前端临时数据库-KeyValue Nosql--SAAS本地化及未来之窗行业应用跨平台架构

一、代码 // /* 未来之窗 client db nosql v1 KV */class CyberWin_Database_NOSQL_KV {constructor() {this.data {};}set(key, value) {this.data[key] value;}get(key) {return this.data[key];}delete(key) {delete this.data[key];} } 二、代码解释 以下是用修仙手法…

力扣763-划分字母区间(Java详细题解)

题目链接:763. 划分字母区间 - 力扣(LeetCode) 前情提要: 因为本人最近都来刷贪心类的题目所以该题就默认用贪心方法来做。 贪心方法:局部最优推出全局最优。 如果一个题你觉得可以用局部最优推出全局最优&#xf…

Web 应用开源项目大全结合巴比达内网穿透

巴比达内网穿透配置 一、引言 无论是家庭用户还是企业用户,内网穿透技术的需求日益增长。巴比达(BabiDa)内网穿透工具以其简单易用的特性,成为了许多用户的首选。本文将详细介绍巴比达内网穿透的配置方法,帮助您轻松实…

经验笔记:Maven 与 Gradle —— Java 构建工具对比

经验笔记:Maven 与 Gradle —— Java 构建工具对比 引言 在 Java 开发过程中,选择合适的构建工具对于提升开发效率、保证构建一致性以及简化项目管理至关重要。Maven 和 Gradle 是目前最常用的 Java 构建工具,它们不仅能够自动化构建过程&a…

GAMES104:10+11游戏引擎中物理系统的基础理论算法和高级应用-学习笔记

文章目录 概览一,物理对象与形状1.1 对象 Actor1.2 对象形状Actor Shape 二,力与运动2.1 牛顿定律2.2 欧拉法2.2.1 显式欧拉法Explicit (Forward) Euler’s Method2.2.2 隐式欧拉法 Implicit (Backward) Euler’s Method2.2.3 半隐式欧拉法 Semi-implici…

图片去噪及边缘检测

一:在Python中,图片去噪可以通过不同的方法实现,这些方法包括使用简单的滤波技术到更复杂的算法,比如高斯模糊、中值滤波、非局部均值去噪(Non-local Means Denoising)等。在这里,我将介绍几种常…

自定义 SpringBoot Starter

文章目录 一、自定义 starter1.1 创建 maven 项目1.2 创建邮件配置属性类1.3 创建模拟邮件发送服务类1.4 创建自动配置类1.5 spring.factories 相关配置1.6 打包成依赖 二、测试项目2.1 创建项目2.2 application.yml 配置2.3 测试应用 参考资料 本文源码位于 java-demos/spring…

[OpenCV] 数字图像处理 C++ 学习——10基本阈值处理 附完整代码

文章目录 前言1.基本阈值处理类型(1)阈值二值化(binary)(2)阈值反二值化(binary_inv)(3)截断(truncate)(4)阈值取零(threshold to zero)(5)阈值反取零(threshold to zero inverted)(6)THRESH_MASK(7)THRESH_OTSU(8)THRESH_TRIANGLE 2.代码实现(1)图像读取…