gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-04 09:30:26 25 4
gpt4 key购买 nike

假设我有一个带有这样方法的 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/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com