synchronized锁失灵?

从Spring的AOP角度看Synchronized锁失效和事务失效的情况 这个锁失效是前年了遇到的一个bug,在一个方法上加了Synchronized,为了避免同一时间产生两条相同的单据,但是很明显,它失效了,不然也不会有今天的这篇文章。出现这个bug的是一个老项目,而且这个bug很早之前就发现了,前人应该也发现了,对数据进行了所谓的特殊处理,所以一直也就那么运行下去。老项目本着能运行就不要乱动的原则,就没怎么处理了。后面需要对这个模块进行重构,就决定把这个问题解决了。当时这个问题看了很久,也分析了很久,没有头绪,想着这个bean是单例的应该是不存在锁不住的情况。 阅读详情

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

1. 并发原因

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

  • 首先校验要复制的方案是否存在。

  • 查询所有已经存在的方案的所有名称。

  • 根据要复制方案的名称生成一个新的方案名称,比如“某某方案-副本”。

  • 新生成的方案是否和已存在的方案名称重名,如果重名,则添加后缀,比如“某某方案-副本(2)”。

  • 最终做新方案的落库操作。

不知道大家有没有看到里面在高并发情况下存在的问题,当步骤五还没有落库,就已经有线程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方案,相比较自己去写分布式锁,更稳定,更成熟。

@Autowired
private 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. 存在的问题以及原因

问题就是以上两种写法都没有生效,但是为什么呢?

在解释这个问题之前,我们首先要弄清楚两个问题:

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

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

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

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

底层实现原理

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

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

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

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

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

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

  • 回滚事务: 如果在被注解的方法执行过程中抛出了异常,并且该异常未被 @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注解的方法,在执行完毕以后,就会提交事务,然后到了调用方法里面,再去释放锁。

 ——EOF——


福利:

后台回复【酒店】可免费领取酒店管理系统源码

3大原因揭秘:为何Redisson中synchronized锁会失灵?性能对比一目了然! 🔥关注墨瑾轩,带你探索编程的奥秘!🚀🔥超萌技术攻略,轻松晋级编程高手🚀🔥技术宝库已备好,就等你来挖掘🚀🔥订阅墨瑾轩,智趣学习不孤单🚀🔥即刻启航,编程之旅更有趣🚀在2025年Q3的高并发系统故障报告中,,某电商平台因错误使用锁机制,导致。本文将从底层原理到实战陷阱,彻底拆解为何在Redisson中使用锁会失灵,并提供,让你的系统性能“起飞”。 阅读详情

相关推荐

Java多线程同步实战:从synchronized到Lock的进化之路

本文深入探讨Java多线程同步技术,从基础的synchronized到高级的Lock接口,详细解析线程同步的核心问题与解决方案。通过实战案例和性能对比,帮助开发者理解竞态条件、内存可见性等关键概念,并掌握ReentrantLock、读写锁等高级特性的应用场景,提升高并发系统的稳定性和性能。

weixin_30133813的博客 330

Synchronized锁失效的几种情况

Synchronized锁失效的几种情况

我就说我500的博客 5420

大模型心理归因能力为何失灵?127个真实情境测试揭示根本缺陷

心理归因是人类理解行为背后动机的核心认知能力,其本质是基于行为、情境与历史的动态因果推理,而非静态知识检索或模式匹配。当前主流大语言模型虽在心理学知识问答(如MMLU)中表现优异,但因缺乏对具身经验、时序弹性与动机嵌套的建模机制,在真实心理推理任务中严重受限。本研究通过127个临床级结构化微情境——如‘孩子打翻牛奶后笑出声’——系统暴露了LLM在情绪归因、反直觉行为解读和多层动机识别上的结构性短板。实验采用Kappa一致性系数与零提示裸机测试,证实模型准确率仅58.3%,远低于人类基线(83.7%)。这些发

weixin_30423977的博客 401

Synchronized-锁失效问题剖析

线程锁-Synchronized

Fong灵 3345

Java开发者面试突围:构建三位一体能力体系,应对场景化面试与AI浪潮

在软件开发领域,扎实的底层核心能力是工程师的立身之本。理解Java内存模型、并发编程原理、JVM垃圾回收机制以及数据库索引与事务隔离级别,是构建高性能、高可靠系统的技术基石。这些原理性知识为解决复杂的工程问题提供了理论支撑,其技术价值在于能够指导开发者进行系统性能优化、故障诊断与架构设计。在电商、金融等高并发应用场景中,能否灵活运用这些知识解决诸如缓存一致性、秒杀系统设计等实际问题,成为衡量工程师能力的关键。随着技术演进,大模型(AI)等新趋势正与现有技术栈深度融合,为开发者带来了效率提升与架构创新的新机遇

weixin_34146805的博客 431

Java后端面试新趋势:从八股文背诵到系统化工程问题解决能力

在Java后端开发领域,技术面试的考察重点正从单纯的知识点记忆转向系统化的工程问题解决能力。理解Java并发编程、JVM性能调优等基础概念是起点,但关键在于如何将这些原理应用于高并发、分布式系统等真实场景。其技术价值在于构建稳定、可扩展的后端服务,满足互联网应用对高性能和高可用的需求。应用场景广泛覆盖电商秒杀、实时交易、大数据处理等业务。本文基于面试实战,探讨如何将零散知识点串联成能扛住压力的系统解决方案,涉及系统设计、故障排查等核心工程实践,帮助开发者应对从技术选型到线上运维的全链路挑战。

weixin_30851867的博客 399

Java线程同步

package work2; public class SynchronizedTest implements Runnable { int num = 50; @Override public void run() { // TODO Auto-generated method stub while(true) { ...

zhicheng_angle的博客 428

10X Genomics单细胞数据分析实战:从FASTQ到UMAP可视化的完整流程

本文详细介绍了10X Genomics单细胞数据分析的完整流程,从FASTQ原始数据处理到UMAP可视化。通过Seurat工具包实现数据质量控制、标准化、降维聚类和细胞注释,特别适合生物信息学新手学习。文章包含可复现的代码示例和常见问题解决方案,帮助研究者快速掌握单细胞测序数据分析的核心技术。

weixin_30952535的博客 538

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

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

一个人的江湖 2303

新手必看:SMIC 0.18um PDK安装保姆级教程(附Cadence IC617环境配置)

本文提供SMIC 0.18um PDK在Cadence IC617环境下的详细安装教程,涵盖环境准备、文件解压、安装脚本运行、环境变量配置及Cadence集成验证等关键步骤,特别针对初学者常见问题提供解决方案,助力快速掌握芯片设计工具配置。

weixin_30522183的博客 496

从零到一:手把手教你用STM32F103C8T6打造个人专属开发板(附立创EDA工程与源码)

本文详细介绍了如何使用STM32F103C8T6微控制器从零开始设计和制作个人专属开发板。内容涵盖硬件选型、原理图设计、PCB布局、功能验证等全流程,并提供了立创EDA工程文件和源码,帮助开发者深入理解STM32开发板的硬件架构与设计要点。

weixin_26802933的博客 287

使用Redisson时,为何synchronized锁会失灵?

来源:https://blog.youkuaiyun.com/wu2374633583???? 欢迎加入小哈的星球,你将获得:专属的项目实战 /1v1 提问/Java 学习路线 /学习打卡/每月赠书/社群讨论新项目:《从零手撸:仿小红书(微服务架构)》正在持续爆肝中,基于 Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...,点击查看项目介绍;《...

犬小哈 219

用LangFuse+LangChain打造可观测的AI应用:从埋点到成本监控实战

本文详细介绍了如何将开源可观测性平台LangFuse深度集成到基于LangChain构建的AI应用中,特别是电商客服机器人场景。通过从基础SDK集成、提示词管理到成本监控的完整实战,帮助开发者实现对LLM应用内部工作流的清晰追踪、问题排查与费用分析,从而打造稳定、可控的生产级AI应用。

dapp9builder的博客 711

深入探讨 Java 中的 synchronized 与 Spring 事务失灵问题

在高并发环境下,优惠券秒杀业务面临的主要挑战在于防止重复下单以及确保库存准确扣减。锁粒度控制:利用用户 ID 的细粒度锁定,确保只有同一用户的请求串行执行,避免全局锁带来的性能瓶颈。事务代理调用:通过获取 Spring AOP 代理对象,确保事务管理注解在业务逻辑中正常生效,防止内部方法调用导致的事务失灵问题。综合实践经验:结合具体业务场景,采用细粒度锁与正确的事务调用顺序,有效提升了系统的并发处理能力和数据一致性。

qq_20769227的博客 1979

焊缝缺陷智能检测:基于 YOLO 的工业质检系统实战

从焊缝缺陷数据集的构建到模型训练,从桌面检测界面到缺陷统计输出,本文覆盖了一套工业焊缝质检系统的完整路线。这个场景的核心难点在数据层面——气孔和裂纹这类小目标的标注质量直接决定了模型的最终表现。建议先在开源数据集上跑通全链路,确认 mAP 达到可接受的水平后,再通过补充特定类型的小目标数据和提升输入分辨率来做精度优化。工业场景中,漏检一个气孔的成本和误报一个假阳性的成本不一样——根据对两类成本的承受力,调整置信度阈值来找到业务上的最佳平衡点。

m0_71589036的博客 202

Python爬虫:实时交通流量数据爬取与路况分析

随着城市化进程的不断推进,交通流量的管理与预测已成为智能交通系统的重要研究方向。交通流量数据不仅为城市交通管理提供了科学依据,还为驾驶员提供了实时路况信息,帮助其做出更加合理的出行决策。通过爬虫技术,我们可以实时获取城市的交通流量数据,从而分析路况,优化交通管理。在这篇博客中,我们将介绍如何使用Python编写一个爬虫来爬取实时交通流量数据,分析道路的交通状况。我们将使用最新的技术工具来构建爬虫,包括。

2201_76125261的博客 1268

Java单例线程安全:从创建到使用的全链路保障

单例模式是Java中最基础的设计模式,但其线程安全性远非‘加个synchronized’就能解决。本质在于:JVM仅保证类加载阶段的初始化原子性,而对象创建后的状态读写、内部集合操作、日期格式化等运行时行为,均需开发者主动构建并发安全机制。理解synchronized锁升级(偏向锁→轻量级锁→重量级锁)、volatile禁止指令重排序、final字段的happens-before语义,是实现高可靠单例的关键。真实场景中,90%的线程安全问题发生在单例使用阶段——如ConcurrentHashMap替代Has

adr5970的博客 502

【图像超分】论文复现:经典超分 | SAN的Pytorch源码复现,跑通源码,适配高版本的python/pytorch/cuda,使用EDSR-PyTorch进行训练和测试

本文介绍了单幅图像超分辨率重建方法SAN(二阶注意网络)的代码实现。文章详细说明了在高版本Python/Pytorch环境下适配SAN源码的方法,并提供了修改后的完整代码和预训练模型下载链接。 主要亮点包括: 将SAN模型实现整合到EDSR-Pytorch框架中,解决了高版本环境下的兼容性问题 提供了完整的数据集准备指南(DIV2K训练集和5个测试集) 给出了详细的测试命令,涵盖x2、x3、x4不同放大倍率 公开了预训练模型和可视化结果的下载资源 提供了修改后的适配高版本环境的代码。

主要更新底层视觉(去噪、超分等)相关的科研内容,形式为【论文精读】+【论文复现】 1414

从jstack到火焰图:现代Java应用CPU 100%排查与治理全链路实践

在Java应用性能调优领域,CPU使用率异常是常见的生产问题。其排查原理通常遵循从系统层到应用层的逐层定位逻辑,即先通过操作系统命令(如top、pidstat)识别高负载进程与线程,再通过Java线程转储(jstack)建立操作系统线程与Java线程的映射关系,最终借助性能剖析工具(Profiler)生成火焰图,精准定位热点代码。这一技术链路的价值在于,它能将模糊的“CPU高”现象,转化为具体的代码行级瓶颈,从而指导有效的性能优化。在微服务与云原生等应用场景中,面对复杂的线程模型与混合负载,掌握这套从现象到

weixin_30565327的博客 402

HFSS实战项目——基片集成波导(SIW)带通滤波器

设计了一款直接耦合的SIW带通滤波器, 采用直接耦合的形式, 工作频率覆盖19~21 GHz, 通带内插入损耗小于1.5 dB, 回波损耗小于-20 dB, 带外抑制特性良好.

2401_87464239的博客 4897

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Java并发编程中的“幻觉”现象:指令重排序、内存可见性与伪共享解析

在并发编程中,指令重排序和内存可见性是理解多线程行为的基础概念。指令重排序是编译器和处理器为提高性能对执行顺序的优化,而内存可见性涉及CPU缓存与主内存的数据同步问题。这些底层机制直接影响程序的正确性,尤其在Java内存模型(JMM)框架下,volatile关键字和happens-before规则提供了关键保障。其技术价值在于确保高并发场景下的数据一致性,避免出现数据“瞬移”或线程死锁。典型应用场景包括生产者-消费者模型、单例模式实现及高性能计数器。本文通过“猫和老鼠”的比喻,生动剖析了由伪共享和JIT优化

weixin_34074740的博客 457

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

Linux服务器时间不准?三层模型诊断与NTP同步实战指南

在计算机系统中,时间同步是确保分布式系统、数据库和日志记录等关键功能正常运行的基础。其核心原理是通过网络时间协议(NTP)来校准因硬件晶振误差导致的系统时钟漂移(Clock Drift),从而维持全局时间的一致性。这项技术的价值在于,它能避免因时间偏差引发的数据不一致、SSL证书验证失败和监控告警失灵等一系列严重故障。在实际应用场景中,无论是单机服务器还是大规模集群,都需要通过配置可靠的NTP时间源来构建稳定的时间体系。本文聚焦于Linux环境下的时间管理,深入解析时区、系统时钟与NTP同步这三个核心概念,

weixin_30555515的博客 312

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

2025年Java开发者成长地图:从核心原理到微服务架构的实战进阶

Java作为企业级应用开发的主流语言,其知识体系涵盖从语言基础到分布式架构的多个层面。理解JVM内存模型与垃圾回收机制是编写高性能应用的基础,而并发编程中的线程池与AQS原理则是解决高并发场景的关键。在微服务架构成为主流的今天,掌握Spring Boot自动配置原理及Spring Cloud服务治理组件,能够帮助开发者构建可扩展、易维护的分布式系统。结合Redis缓存与消息队列等中间件,可以有效提升系统吞吐量与可靠性。本文围绕Java核心、微服务架构及工程实践,为开发者提供一套从原理到实战的成长策略,助力应

cuanji3287的博客 626
上一篇: 数据库性能调优:定位Slow SQL!
下一篇: 【源码分享】社团管理系统!
架构文摘JGWZ
博客等级 码龄5年 8808粉丝 345原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值