Java线程条件Condition对象
通常,线程进入临界区后却发现只有满足了某个条件之后它才能执行。可以使用一个条件对象来管理那些已经获得了一个锁却不能做有用工作的线程。
现在来模拟银行的转账
// 判断from账户的余额是否大于等于要转账的金额
if (bank.getBalance(from) >= amount)
bank.transfer(from, to, amount);
加入可以成功通过这个测试,但在调用transfer方法之前,当前线程完全有可能被中断。
// 判断from账户的余额是否大于等于要转账的金额
if (bank.getBalance(from) >= amount)
// 此时线程可能已停用
bank.transfer(from, to, amount);
在线程再次运行前,账户余额可能已经低于提款金额。必须确保在检查余额与转账活动之前没有其他线程修改余额。为此,可以使用一个锁来保护这个测试和转账操作:
public void transfer(int from, int to, int amount){
bankLock.lock();
try {
while (accounts[from] < amount){
// wait
...
}
// transfer funds
...
}
finally {
bankLock.unlock();
}
}
现在,当账户中没有足够的资金时,我们应该做什么呢?我们要等待,知道另一个线程向账户中增加了资金。但是,这个线程刚刚获得了对bankLock的排他性访问权,因此别的线程没有增加存款的机会。这里就要引入条件对象。
一个锁对象有一个或多个相关联的条件对象。可以用newCondition方法获得一个条件对象。
class Bank {
private Lock bankLock = new ReentrantLock();// 重入锁
private Condition sufficientFunds;
...
public Bank(){
...
sufficientFunds = bankLock.newCondition();
}
}
如果transfer方法发现资金不足,它会调用
sufficientFunds.await();
当前线程现在暂停,并放弃锁。这就允许另一个线程执行,我们希望它能增加账户余额。
等待获得锁的线程和已经调用await方法的线程存在本质上的不同。一旦一个线程调用了await方法,它就进入这个条件的等待集(wait set)。当锁可用时,该线程并不会变成可运行状态。实际上,它仍保持非活动状态,直到另一个线程在同一条件上调用signalAll方法。
当另一个线程完成转账时,它应该调用
sufficientFunds.signalAll();
这个调用会重新激活等待这个条件的所有线程。当这个线程从等待集中移出时,它们再次成为可运行的线程,调度器最终将再次将它们激活。同时,它们会尝试重新进入该对象。一旦锁可用,它们中的某个线程将从await调用返回,得到这个锁,并从之前暂停的地方继续执行。
此时,线程应该再次测试条件。不能保证现在一定满足条件——signalAll方法仅仅是通知等待的线程,现在有可能满足条件,值得再次检查条件。
最终需要有某个其他线程调用signalAll方法,这一点至关重要。当一个线程调用await时,它没有办法重新自行激活。它寄希望于其他线程。如果没有其他线程来重新激活等待的线程,它就永远不再运行了。这将导致令人不快的死锁(deadlock)现象。
应该什么时候调用signalAll呢?从经验上将,只要一个对象的状态有变化,而且可能有利于等待的线程。就可以调用signalAll方法。例如,当一个账户余额发生改变时,就应该再给等待的线程一个机会来检查余额。在这个例子中,完成转账时,我们就会调用signalAll方法。
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();
}
}
注意signalAll调用不会立即激活一个等待的线程。它只是解除等待线程的阻塞,使这些线程可以在当前线程释放锁之后竞争访问对象。
另一个方法signal只是随机选择等待集中的一个线程,并解除这个线程的阻塞状态。这比解除所有线程的阻塞更高效,但也存在风险。如果随机选择的线程发现自己仍然不能运行,它就会再次阻塞。如果没有其他线程再次调用signal,系统就会进入死锁。

浙公网安备 33010602011771号