带有同步关键字的 Spring @Transactional 不起作用 [英] Spring @Transactional with synchronized keyword doesn't work

查看:26
本文介绍了带有同步关键字的 Spring @Transactional 不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

Let's say I have a java class with a method like this (just an example)

@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");
        }
    }
}

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

Say this book gets removed and then re-added with a new author or other minor change, so this method might get called twice very fast from another system, first to remove the Book, then to add the same Book back (with some new details).

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

To handle this we might try (like I did) to add the above @Transactional code, and then also 'synchronized' when the @Transactional doesn't work. But strangely it fails on the second call with

无法添加图书 - 图书已存在".

"Cannot add book - book already exist".

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

I spent a lot of time trying to figure this out, so thought I'd share the answer.

推荐答案

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

When removing and immediately adding back a Book, if we have no "@Transactional" or "synchronized" we are starting with this thread execution:

T1:|-----移除书----->

T1: |-----remove book----->

T2:|-------加书------->

T2: |-------add book------->

synchronized 关键字确保方法只能由一个线程运行.这意味着执行变成这样:

The synchronized keyword makes sure that the method can only be run by one thread at a time. This means execution becomes this:

T1:|-----删除书籍-----> T2:|--------添加书籍------>

T1: |-----remove book-----> T2: |--------add book------>

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

The @Transactional annotation is an Aspect, and what it does is that it creates a proxy java class around your class, adds some code (begin transaction) to it before the method call, calls the method and then calls some other code (commit transaction). So the first thread now looks like this:

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

T1: |--Spring begins transaction--|-----remove book----- |--Spring commits transaction --->

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

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

第二个线程是这样的:

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

T2: |--Spring begins transaction--|-------add book------- |--Spring commits transaction --->

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

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

请注意,@Transactional 注释只会锁定数据库中的同一实体不被并发修改.由于我们添加了一个不同的实体(但具有相同的书名),所以它没有多大用处.但它仍然不应该受到伤害吗?

Notice that the @Transactional annotation only locks the same entity in a database from being modified concurrently. Since we are adding a different entity (but with the same book name), it doesn't do much good. But it still shouldn't HURT right?

这里是有趣的部分:

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

The transaction code that Spring adds is not part of the synchronized method, so the T2 thread can actually start its method before the "commit" code is finished running, right after the first method call is done. Like this:

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

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

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

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

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

So. when the "add" method reads the database, the remove code has been run, but NOT the commit code, so it still finds the object in the database and throws the error. milliseconds later it will be gone from the database though.

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

Removing the @Transactional annotation would make the synchronized keyword work as expected, although this is not a good solution as others have mentioned. Removing synchronized and fixing the @Transactional annotation is a better solution.

这篇关于带有同步关键字的 Spring @Transactional 不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆