java - 带有同步关键字的 Spring @Transactional 不起作用

标签 java spring synchronized transactional

假设我有一个带有这样方法的 java 类(只是一个例子)

@Transactional
public synchronized void onRequest(Request request) {

    if (request.shouldAddBook()) {
        if (database.getByName(request.getBook().getName()) == null) {
            database.add(request.getBook());
        } else {
            throw new Exception("Cannot add book - book already exist");
        }
    } else if (request.shouldRemoveBook()) {
        if (database.getByName(request.getBook().getName()) != null) {
            removeBook();
        } else {
            throw new Exception("Cannot remove book - book doesn't exist");
        }
    }
}

假设这本书被删除,然后用新作者或其他小的更改重新添加,所以这个方法可能会被另一个系统非常快地调用两次,首先是删除这本书,然后再添加同一本书(有一些新的细节)。

为了解决这个问题,我们可能会尝试(像我一样)添加上面的 @Transactional 代码,然后在 @Transactional 不起作用时也“同步”。但奇怪的是它在第二次调用时失败了

“无法添加书籍 - 书籍已存在”。

我花了很多时间试图弄清楚这一点,所以我想我会分享答案。

最佳答案

删除并立即添加回来时 一本书 ,如果我们没有“@Transactional”或“synchronized”,我们将从这个线程执行开始:

T1:|-----取书----->

T2:|-------加书------->
synchronized关键字确保 方法只能由 运行一次一个线程 .这意味着执行变成这样:

T1:|-----删除书籍-----> T2:|--------添加书籍------>
@Transactional注释是一个方面,它的作用是创建 代理 java 类围绕您的类,在方法调用之前添加一些代码( 开始事务 ),调用该方法然后调用一些其他代码( 提交事务 )。所以第一个线程现在看起来像这样:

T1:|--Spring开始事务--|-----remove book-- |--Spring提交事务--->

或更短:T1:|-B-|-R-|-C-->

和第二个线程是这样的:

T2:|--Spring开始事务--|-------添加书籍-- |--Spring提交事务--->

T2:|-B-|-A-|-C-->

请注意 @Transactional注解仅锁定数据库中的同一实体不被并发修改。由于我们添加了一个不同的实体(但具有相同的书名),它没有多大用处。但它仍然不应该伤害对吧?

那么有趣的部分是:

Spring添加的事务码不是同步方法的一部分 ,所以 T2 线程实际上可以在“提交”代码运行完成之前启动它的方法,就在第一个方法调用完成之后。像这样:

T1:|-B-|-R-|-C--|-->

T2:|-B------|-A-|-C-->

所以。当“add”方法读取数据库时,删除代码已经运行,但不是提交代码,所以它仍然在数据库中找到对象并抛出错误。几毫秒后,它将从数据库中消失。

删除 @Transactional注释将使 synchronized关键字按预期工作,尽管这不是其他人提到的好的解决方案。删除 synchronized并修复 @Transactional注释是一个更好的解决方案。

关于java - 带有同步关键字的 Spring @Transactional 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41767860/

相关文章:

java - 学习Java,同步关键字的使用

java - 带有两个同步块(synchronized block)的 DCL 损坏了?

Java CryptUnprotectData Windows WiFi 密码

java - Spring Boot 2.0 JNDI 属性值未从应用程序测试属性文件加载

java - 获取 IgniteCheckedException : Default Ignite instance has already been started exception when enabling Persistence on single Node

java - key 斗篷 : Get notified via API when a new user registers

java - 同步方法是否在与 UI 线程 (Android) 不同的线程上运行?

java - 在java中读取和写入.PPM图像?

java - 无法通过 jmx-exporter 从 activemq 中抓取指标

java - Android,这些代码有什么区别?