带有同步关键字的Spring @Transactional无法正常工作 [英] Spring @Transactional with synchronized keyword doesn't work

查看:160
本文介绍了带有同步关键字的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"或已同步",则从此开始线程执行:

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开始事务处理-| -----删除书本----- | --Spring提交事务处理--->

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

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

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

和第二个线程是这样的:

and the second thread like this:

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天全站免登陆