Bank 类有一个特殊要求:如果要求它从一个账户向另一个账户转移一笔钱,而源账户上没有足够的钱,它必须等到存入足够的钱才能进行转账可能的。您可以运行一个循环来检查每次迭代是否有足够的钱,并仅在满足此条件时获取锁:
while (true) {
if (accounts[from] >= amount) {
bankLock.lock();
try {
if (account[from] >= amount) {
// do the transfer here...
break;
}
} finally {
bankLock.unlock();
}
}
}
但是这种方法:
- 在不断的检查中浪费 CPU 资源(它没有人
在几小时或几天内存入足够的钱?)
- 看起来笨重且不习惯
- 并不总是有效(原因不在本问题的范围内,如果您有兴趣,我可以提供 cmets 中解释的链接)
因此,您需要一些机制来告知您只是在等待帐户中的某些更改。如果没有人将钱存入其中,则一次又一次地检查帐户中的金额是很浪费的。还有更多 – 您还需要在有人存入资金后立即获得锁定,这样您就可以专门检查新帐户状态并决定是否可以进行转账。
您还必须记住,存款并不是账户上唯一允许的操作。例如,还有提款。如果有人提款,则无需检查您的账户是否有可能转帐,因为我们确信现在的钱更少了。因此,我们希望在存款时被唤醒,但不希望在提款时被唤醒。我们需要以某种方式将它们分开。这就是条件发挥作用的地方。
Condition 是一个实现Condition 接口的对象。它只是一种抽象,允许您将锁定/等待逻辑划分为多个部分。在我们的例子中,我们可能有两个条件:一个用于增加账户余额,另一个用于减少(例如,如果有人正在等待将银行账户归零以关闭它):
sufficientFunds = bankLock.newCondition();
decreasedFunds = bankLock.newCondition();
现在,您无需在循环中进行大量检查,您可以组织您的程序,让您只有在有人为帐户注资时才醒来并检查帐户:
private final double[] accounts;
private Lock bankLock;
private Condition sufficientFunds;
public void transfer(int from, int to, int amount) {
bankLock.lock();
try {
while (accounts[from] < amount) {
sufficientFunds.await();
}
// transfer funds ...
sufficientFunds.signalAll();
} finally {
bankLock.unlock();
}
}
public void deposit(int to, int amount) {
bankLock.lock();
try {
// deposit funds...
sufficientFunds.signalAll();
} finally {
bankLock.unlock();
}
}
那么,让我们看看这里发生了什么并回答您的问题:
transfer() 正在尝试获取bankLock。如果有人已经持有这个锁,你的线程就会被另一个线程阻塞,直到锁被释放。
当另一个线程释放锁时,您获取它并可以检查帐户的状态。如果钱不够,你决定坐等有人把钱存入账户,所以你打电话给sufficienFunds.await()。它使您的线程等待某事发生。你决定这样做,不仅仅是因为另一个线程而被阻塞,这就是 blocked 和 waiting 之间的区别。但是你是对的,在这两种情况下你的线程都没有运行,所以区别更符合逻辑,而不是技术。
在bankLock 上创建的条件上调用await() 会释放此锁并使其可供其他线程获取和执行操作。如果不释放锁,您的线程将阻塞银行中的所有操作并导致死锁。
现在,当有人对帐户进行任何更改以增加金额时,它会通知sufficientFunds 条件,我们的传输线程唤醒并且循环可以进行另一次检查。在这里,我们有两件事要看。首先,当我们的线程唤醒时,它会自动重新获取锁,因此我们可以确保我们可以安全地排他地进行检查和修改。其次,可能会出现新的金额仍然不足以进行转移(在条件上发出信号仅表示发生了某些事情,可能会改变您正在等待的状态,但不能保证该状态)。在这种情况下,我们必须等待下一次存款。这就是为什么我们必须使用while 循环,而不是简单的if。
当账户里的钱终于足够时,我们进行转账。我们在转账后也会打电话给sufficienFunds.signalAll(),因为我们增加了to账户中的金额。如果其他线程正在等待该帐户的资金,它会获取锁并可以使其工作。
因此,使用锁和条件可以让您以安全有效的方式组织多线程程序。