记录一次Redisson使用synchronized和分布式锁不生效的原因

最近在开发的过程中,遇到了一个并发场景,用户进行方案复制的时候,当快速点击两次操作的时候,出现了复制方案重名的情况,实际上是复制方案的方案名称,是由后端根据数据库已有的方案名称和当前要复制的方案名称进行逻辑处理,保证方案名称不能重复,比如:要复制的方案名称为“我的方案”,那么复制得到的方案名称为“我的方案-副本”,在高并发场景下,就会出现重名情况。

1. 并发原因

每次在复制方案的时候,会有如下步骤:

  1. 首先校验要复制的方案是否存在。
  2. 查询所有已经存在的方案的所有名称。
  3. 根据要复制方案的名称生成一个新的方案名称,比如“某某方案-副本”。
  4. 新生成的方案是否和已存在的方案名称重名,如果重名,则添加后缀,比如“某某方案-副本(2)”。
  5. 最终做新方案的落库操作。

不知道大家有没有看到里面在高并发情况下存在的问题,当步骤五还没有落库,就已经有线程2进来,执行了查询操作,最后线程2落库生成的名称就会和线程1生成的方案名称重复。

@Transactional(rollbackFor = Exception.class)
public  void xxxCopy(Long modelId) throws GseException {//业务逻辑代码
}

2. 初步解决办法

2.1 本地锁方式

我在本地做了两种尝试,首先通过本地锁(比如synchronized,Lock)相关手段进行锁定,当然这种肯定不能上生产,因为当多节点部署的时候,这种本地锁没有任何意义。

@Transactional(rollbackFor = Exception.class)
public  void xxxCopy(Long modelId) throws GseException {synchronized (this) {//业务逻辑代码}
}

这种写法没有生效,在我进行本地压测,开启多个线程的情况下,还是出现了重名情况,具体原因我待会会给大家分析。

2.2 分布式锁

这种才是生产上高并发经常会用到的,因为生产时多prod,采用本地锁没有任何意义,分布式锁我采用的是Redisson方案,相比较自己去写分布式锁,更稳定,更成熟。

    @Autowiredprivate RedissonClient redissonClient;private static final String REDIS_COST_MODEL_ID_LOCK = "redis_cost_model_id_lock";@Transactional(rollbackFor = Exception.class)
public  void xxxCopy(Long modelId) throws GseException {RLock lock = redissonClient.getLock(REDIS_COST_MODEL_ID_LOCK + modelId);try {if (lock.tryLock(20, 2, TimeUnit.SECONDS)) {//业务逻辑代码} else {log.error("获取分布式锁失败");}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 判断当前线程是否持有锁if (lock.isHeldByCurrentThread()) {//释放当前锁lock.unlock();log.info(Thread.currentThread().getName() + "释放锁" + LocalDateTime.now());}}
}

但是,这种写法没有生效,在我本地压测的时候,还是存在重名问题。

3. 存在的问题以及原因

问题就是以上两种写法都没有生效,但是为什么呢?
在解释这个问题之前,我们首先要弄清楚两个问题:

  1. @Transactional的底层实现原理,开启事务和提交事务的时机是什么?
  2. 分布式锁,和本地锁机制释放锁的时机是什么时候?

3.1 @Transactional的底层实现原理,开启事务和提交事务的时机是什么?

它的底层实现原理主要依赖于 Spring 的面向切面编程(AOP)机制。
底层实现原理

  1. AOP 代理:当一个类或方法被 @Transactional 注解标记时,Spring 容器在初始化 Bean 时会检测到这个注解。对于使用 Spring 的代理模式(如 JDK 动态代理或 CGLIB),Spring 会为该 Bean 创建一个代理对象。这个代理对象会在调用实际方法前后插入事务管理相关的代码,即在方法执行前开启事务,在方法执行完毕后根据执行情况提交或回滚事务。

  2. 解析注解:Spring 通过扫描 Bean 定义,识别出带有 @Transactional 注解的方法或类,并配置相应的事务属性,如传播行为、隔离级别、超时时间、是否只读等。

  3. 事务拦截器:Spring 使用 AOP 机制中的拦截器(Interceptor)或Advice(通常为 TransactionInterceptor 或 AspectJ 的切面),在方法调用前后织入事务处理逻辑。在方法调用前,根据事务属性设置事务的开始;在方法正常结束时提交事务,如果方法抛出未检查异常(继承自 RuntimeException 的异常)或已检查异常(被 @Transactional 的 rollbackFor 属性指定的异常)则回滚事务。

开启事务和提交事务的时机

  1. 开启事务:事务通常在进入被 @Transactional 注解的方法之前立即开始。这意味着在执行业务逻辑之前,Spring 会确保与当前环境匹配的事务上下文已经建立。这包括选择合适的事务管理器,根据事务属性配置事务的隔离级别、传播行为等,并在数据库中实际开启事务。

  2. 提交事务:如果被注解的方法正常执行结束,没有抛出任何异常,Spring 会在离开该方法之前提交事务。提交事务意味着将所有挂起的更改永久化到数据库中,使事务中的所有操作对外可见。

  3. 回滚事务:如果在被注解的方法执行过程中抛出了异常,并且该异常未被 @Transactional 的 noRollbackFor 属性豁免,Spring 将在捕获到异常后立即回滚事务,撤销所有在事务中已完成但未提交的操作,保持数据的一致性。

3.2 分布式锁,和本地锁机制释放锁的时机是什么时候?

答案是:本地锁,如果是synchronized,看你包裹起来的范围。Lock的话 看你手动释放锁的时候。
分布式锁:看你手动释放锁的时候。

那么造成问题的原因就出来了,如下图:

在这里插入图片描述
也就是说最终提交事务和释放锁的顺序有问题,按照上面的代码写法,因为当只有方法执行完了,AOP切面才会提交事务,那么如果你将上锁的代码写到被@Transactional注解的方法里面,那么提交事务永远都会处于释放锁之后,那么在释放锁之后,提交事务之前的这段时间,就会有并发问题。

4. 正确的写法

4.1 本地锁

    public  void addLock(Long modelId)throws GseException {synchronized (this) {xxxCopy(modelId);}}@Transactional(rollbackFor = Exception.class)public  void xxxCopy(Long modelId) throws GseException {synchronized (this) {//业务逻辑代码}}

4.2 分布式锁

public void addLock(Long modelId) throws GseException {RLock lock = redissonClient.getLock(REDIS_COST_MODEL_ID_LOCK + modelId);try {if (lock.tryLock(20, 2, TimeUnit.SECONDS)) {xxxCopy(modelId);} else {log.error("获取分布式锁失败");}} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 判断当前线程是否持有锁if (lock.isHeldByCurrentThread()) {//释放当前锁lock.unlock();log.info(Thread.currentThread().getName() + "释放锁" + LocalDateTime.now());}}}@Transactional(rollbackFor = Exception.class)
public  void xxxCopy(Long modelId) throws GseException {synchronized (this) {//业务逻辑代码}
}

这样的写法,成功避免了并发问题,被@Transactional注解的方法,在执行完毕以后,就会提交事务,然后到了调用方法里面,再去释放锁。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/18800.shtml

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

相关文章

git新电脑下载配置记录

1、官网下载 2、安装 3、配置 (1)先生成密钥 ssh-keygen -t rsa -C “XXXqq.com” (2)生成两个文件,复制.pub,在GitHub的setting里面设置ssh, (3)验证远程是否配置成功 ssh -T g…

Python轻量级的插件框架库之pluginbase使用详解

概要 在软件开发中,插件系统是一个常见的需求。插件系统允许开发者动态加载和卸载功能模块,从而提高应用程序的灵活性和可扩展性。Python的pluginbase库是一个轻量级的插件框架,旨在简化插件系统的构建过程。pluginbase库提供了一套简单易用的API,使开发者能够快速集成插件…

【408真题】2009-23

“接”是针对题目进行必要的分析,比较简略; “化”是对题目中所涉及到的知识点进行详细解释; “发”是对此题型的解题套路总结,并结合历年真题或者典型例题进行运用。 涉及到的知识全部来源于王道各科教材(2025版&…

Windows电脑高颜值桌面便利贴,便签怎么设置

在这个看颜值的时代,我们不仅在衣着打扮上追求时尚与美观,就连电脑桌面也不愿放过。一张唯美的壁纸,几款别致的小工具,总能让我们的工作空间焕发出不一样的光彩。如果你也热衷于打造高颜值的电脑桌面,那么,…

探索智能零售的未来商机与运营策略

探索智能零售的未来商机与运营策略 在智能零售的广阔图景中,无人售货机加盟赫然矗立为一股不可小觑的力量,预示着零售业态未来的转型与机遇。其核心优势多维展开,具体阐述如下: 1. **全天候服务**:无人售货机的运行跨…

优思学院:什么是DMADV模式?和DMAIC有何区别?

在现代企业管理中,质量管理是一项至关重要的工作。六西格玛管理法作为一种高效的质量管理方法,已在全球范围内得到了广泛应用。它不仅在制造业中发挥了巨大的作用,在服务业和其他行业中也同样表现出了强大的生命力。六西格玛管理法主要有两种…

【30天精通Prometheus:一站式监控实战指南】第10天:blackbox_exporter从入门到实战:安装、配置详解与生产环境搭建指南,超详细

亲爱的读者们👋   欢迎加入【30天精通Prometheus】专栏!📚 在这里,我们将探索Prometheus的强大功能,并将其应用于实际监控中。这个专栏都将为你提供宝贵的实战经验。🚀   Prometheus是云原生和DevOps的…

Java——执行流程

一、执行流程 1、示例 //第一个Java程序 public class Hello{public static void main(String[] args){System.out.println("Hello World!");} } 编译: 执行: 我们可以看到这里的是类名,而不是字节码文件名 Hello.class &#…

通过 coze 快速构建自己的智能体机器人

通过 coze 快速构建自己的智能体机器人 coze 的使用 一)coze 是什么 「Coze 扣子」AI Bot 开发平台。任何用户都可以快速、低门槛地搭建自己的 Chatbot,且平台支持用户将其一键发布到飞书、微信公众号、豆包等渠道。 二)coze 怎么注册 1. …

【网络层】ICMP 因特网控制协议

文章目录 ICMP 含义以及作用ICMP协议解析结合ICMP协议和ping常见问题 ICMP 含义以及作用 ICMP:Internet control massage protocol 因特网控制协议 Internet控制报文协议ICMP是网络层的一个重要协议。 ICMP协议用来在网络设备间传递各种差错和控制信息,…

QT6.2.4 MSVC2019 连接MySql5.7数据库,无驱动问题

1.下载 查询一下数据库驱动 qDebug()<<QSqlDatabase::drivers(); 结果显示&#xff0c;没有QMYSQL的驱动。 QList("QSQLITE", "QMARIADB", "QODBC", "QPSQL") MySql6.2.4驱动下载地址&#xff0c;如果是别的版本&#xff0c;…

用易查分制作研学活动报名,支持在线签名,一键导出报名统计表格!

学校组织研学活动时&#xff0c;需要家长扫码在线填写报名信息&#xff0c;确认安全承诺和手写签名&#xff0c;提交报名后希望分配报名号&#xff0c;应该如何实现&#xff1f; 易查分的新建填表功能就可以实现上述需求&#xff0c;下面就来教大家如何制作吧。 &#x1f4cc;使…

【LORA协议栈】工作记录

一、硬件资源 MCU型号&#xff1a;STM32F401xE。Lora芯片&#xff1a;SX1276。硬件看门狗。ATT7022E三相电能专用计量芯片。 二、功能简介 作为一个组件&#xff0c;通过485与网关或者各种子设备连接在一起。支持boot升级。通过SPI与LORA芯片通信。接收和发送数据。有3路通信…

Java设计模式-活动对象与访问者

活动对象 Java设计模式中&#xff0c;活动对象是指一个对象始终处于活动的状态&#xff0c;该对象包括一个线程安全的数据结构以及一个活跃的执行线程。 如上所示&#xff0c;ActiveCreature类的构造函数初始化一个线程安全的数据结构&#xff08;阻塞队列&#xff09;、初始化…

Mac | Mac M 芯片应用意外退出问题

现象问题 电脑配置&#xff1a;MacBook Pro M1&#xff0c;系统 Sonoma 很多小伙伴新买了 M 芯片的 MacBook&#xff0c;在下载下应用后进行安装&#xff0c;安装成功后却无法打开&#xff0c;提示意外退出。报错如图 原因 部分应用过适配了 M 芯片&#xff0c;但还是有些应…

乡村振兴与农业科技创新:加大农业科技研发投入,推动农业科技创新,促进农业现代化和美丽乡村建设

一、引言 在当代中国&#xff0c;乡村振兴已成为国家发展的重要战略之一。作为国民经济的基础&#xff0c;农业的发展直接关系到国家的稳定和人民的福祉。随着科技的不断进步&#xff0c;农业科技创新在推动农业现代化和美丽乡村建设中发挥着越来越重要的作用。本文旨在探讨如…

TPK系列——2W 3KVDC 隔离单,双输出 DC/DC 电源模块

TPK系列是一款2W并且有高隔离电压要求的理想产品&#xff0c;工业级温度范围–40℃到 105℃&#xff0c;在此温度范围内都可以稳定输出2W&#xff0c;并且效率非常高&#xff0c;高达89%&#xff0c;同时负载调整率非常低&#xff0c;对于有输出电压精度有要求的地方特别合适&a…

桌面上怎么记工作任务更加合理?能设置桌面提醒的便签软件

在快节奏的现代工作中&#xff0c;电脑已成为我们处理工作的主要工具。每天&#xff0c;我们都要面对电脑屏幕&#xff0c;处理大量的工作任务。为了更好地管理这些琐碎却重要的工作&#xff0c;将工作任务直接记录在桌面上&#xff0c;随时查看和调整&#xff0c;无疑是一种高…

北斗有源终端如何助力防灾应急通信

在自然灾害的严峻挑战面前&#xff0c;通信技术的可靠性成为了决定救援成功与否的关键。北斗有源终端作为先进的卫星通信工具&#xff0c;以其独特的优势和功能&#xff0c;正逐渐在防灾应急通信领域展现出其不可或缺的作用。通过其高精度的定位、稳定的信号传输和广泛的应用场…

如何利用Firebase Hosting来托管网站

文章目录 如何利用Firebase Hosting来托管网站前提条件详细步骤1. 安装 Firebase CLI2. 登录 Firebase3. 初始化 Firebase 项目4. 准备网站文件5. 部署到 Firebase6. 配置自定义域名&#xff08;可选&#xff09; 常见问题 如何利用Firebase Hosting来托管网站 以下是更详细的…