当前位置:  开发笔记 > 编程语言 > 正文

使用synchronized关键字的Spring @Transactional不起作用

如何解决《使用synchronized关键字的Spring@Transactional不起作用》经验,为你挑选了2个好方法。

假设我有一个类似这样的方法的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");
        }
    }
}

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

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

"无法添加书本已经存在".

我花了很多时间试图解决这个问题,所以我想我会分享答案.



1> JavaDevSwede..:

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

T1:| -----删除书----->

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

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

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

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

T1:| --Spring开始事务 - | -----删除书----- | - 弹簧提交事务--->

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

和第二个线程是这样的:

T2:| - 春天开始交易 - | -------添加书------- | - 春天提交交易--->

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

请注意,@Transactional注释仅锁定同时修改数据库中的同一实体.由于我们正在添加一个不同的实体(但具有相同的书名),因此它没有多大好处.但它仍然不应该HURT对吗?

在这里是有趣的部分:

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

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

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

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

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



2> Avinash..:

您需要设置事务隔离级别,以防止对数据库的脏读,而不用担心线程安全。

@Transactional(isolation = Isolation.SERIALIZABLE)
public 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");
        }
    }
}

这是事务传播和隔离的出色解释。

Spring @Transactional-隔离,传播

推荐阅读
小白也坚强_177
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有