繁体   English   中英

线程锁定顺序与简单的同步块

[英]Thread lock ordering versus a simple synchronized block

在阅读Java Concurrency in Practice一书时,遇到了这段代码,其中“fromAccount”和“toAccount”对象被一个接一个地锁定,以防止动态锁定命令死锁。

public void transferMoney(Account fromAccount,Account toAccount) {
    **synchronized (fromAccount) {**
        **synchronized (toAccount) {**
               ........
        }
    }
}

我很困惑为什么需要这种锁定顺序。如果我们只是想确保两个对象同时被锁定,那么如果只有一个常规的同步块,你就不会得到相同的效果访问fromAccount和toAccount对象。 我相信我在这里错过了一些基本概念。 谢谢您的帮助。

public void transferMoney(Account fromAccount,Account toAccount) {
    synchronized (this) {
        fromAccount.someMethod();
        toAccount.someMethod();        
    }
}

你想要避免使用锁定顺序示例的替代方法:拥有一个中央锁,一切都在使用,因为那时你没有得到并发传输,一切都等待一个锁,一次只能进行一次传输。 目前尚不清楚this是什么或它的范围可能是什么,但如果有这种转移服务的多个实例,那么锁定没有任何好处,因为涉及一个帐户的一个转移可以通过一个实例而另一个涉及该转移的转移帐户可以通过另一个。 因此,似乎只能存在其中一个,这会使您的并发性一次性减少到一次传输。 你不会死锁,但你也不会很快处理很多转移。

这个玩具示例背后的想法(你不应该误解任何人如何转移资金)是因为它试图通过锁定转移中涉及的个人账户来获得更好的并发性,因为对于大量的转移,所涉及的账户都不是参与其他并发传输,您希望能够同时处理它们,并通过最小化锁定到各个帐户的锁定范围来最大化并发性。 但是,如果某个帐户涉及多个并发传输,并且对于某些传输以不同的顺序获取锁,则此方案会遇到麻烦。

首先,应该注意的是你带来的例子(基于你的评论,它是第208页,列出10.2)是一个糟糕的例子 - 一个以死锁结束的例子。 对象不会一个接一个地锁定以防止动态锁定顺序死锁,它们是动态锁定顺序发生的示例!

现在,你的建议锁定在this ,但是这是什么this反正,什么是锁定的范围是什么?

  • 很明显,同一个对象必须用于所有操作 - 撤销,存款,转移。 如果使用单独的对象,则一个线程可以在账户A上存款,而另一个线程从账户A转账到账户B,并且他们将不会使用相同的锁定,因此余额将受到损害。 因此,对同一帐户的所有访问的锁定对象应该是相同的。
  • 正如Nathan Hughes解释的那样,人们需要将锁定本地化 我们不能为所有帐户使用一个中央锁定对象,或者我们将让它们全部等待彼此,尽管实际上并没有使用相同的资源。 因此,使用中央锁定对象也是不可能的。

所以看来我们需要本地化锁,以便每个帐户的余额都有自己的锁,以便允许不相关帐户之间的并行操作,但是这个锁必须用于所有操作 - 撤销,存入和转移。

问题就出现了 - 当它只是撤销或存款时,您只在一个帐户上操作,因此您只需锁定该帐户即可。 但是当你转移时 ,你有两个对象。 所以你需要锁定它们的余额,以防有其他线程想要操作。

对两个或多个帐户持有单个锁的任何对象都将破坏上述两点之一。 要么它不会用于所有操作,要么它不会足够本地化。

这就是他们试图一个接一个地锁定两个锁的原因。 他们的解决方案是使Account对象本身成为Account的锁定 - 它满足“所有操作”条件和“位置”条件。 但在我们转账之前,我们仍然需要确保我们拥有两个账户的锁。


但同样,这个源代码是一个容易出现死锁的代码 这是因为一个线程可能想要从帐户A转移到帐户B,而另一个线程想要从帐户B转移到帐户A.在这种情况下,第一个锁定A帐户,第二个锁定B帐户,然后他们陷入僵局,因为他们以相反的顺序执行了锁定。

这里的基本原则是避免竞争条件 在您的情况下,如果在任何其他类中还有另一种方法也在转账到账户,那么不正确的金额可能会在toAccount中更新。 例如,有2个班级进行汇款。

一个类有一个方法:

public void transferMoney(Account fromAccount,Account toAccount) {
    synchronized (this) {
        fromAccount.someMethod();
        toAccount.someMethod();        
    }
}

和其他类包含:

public void transferMoneyNow(Account fromAccount1,Account toAccount) {
    synchronized (this) {
        fromAccount1.someMethod();
        toAccount.someMethod();        
    }
}

如果两种方法同时发生,由于竞争条件不正确,金额可能会更新到帐户。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM