条件对象

  • 通常,线程进入临界区,却发现在某一条件满足之后它才能执行。要使用一个条件对象来管理那些已经获得了一个锁但是却不能做有用工作的线程。
  • 细化银行转账模拟程序,当账户余额不足以转出时,需要等待另一个线程向账户中注入资金。但是,这一线程刚刚获得了对bankLock的排它性访问,因此别的线程没有存款操作的机会。这时需要考虑条件对象。
  • 一个锁对象可以有一个或多个相关的条件对象。
public class Bank {
    private final double[] accounts;
    //创建ReentrantLock对象
    private Lock bankLock = null;
    //对象条件,当余额不足时调用await
    private Condition sufficientFunds;

    public Bank(int n, double initialBalance)
    {
        accounts = new double[n];
        Arrays.fill(accounts, initialBalance);
        bankLock = new ReentrantLock();
        sufficientFunds = bankLock.newCondition();
    }

    //把钱从一个帐户转到另一个帐户
    public void transfer(int from, int to, double amount)
    {
        bankLock.lock();
        try {
            try{
                while (accounts[from] <amount){
                    sufficientFunds.await();
                }
                if (accounts[from] < amount) return;
                System.out.print(Thread.currentThread());
                accounts[from] -= amount;
                System.out.printf(" %10.2f from %d to %d", amount, from, to);
                accounts[to] += amount;
                System.out.printf(" Total Balance: %10.2f%n", getTotalBalance());
                sufficientFunds.signalAll();
            }
            finally {
                bankLock.unlock();
            }
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
  • 如果transfer方法发现余额不足,它调用sufficientFunds.await();。当前线程现在被阻塞了,并放弃了锁。我们希望这样可以使得另一个线程可以进行增加账户余额操作。
  • 等待获得锁的线程和调用await方法的线程存在着本质上的不同。一旦一个线程调用await方法,它进入该条件的等待集。当锁可用时,该线程不能马上解除阻塞。相反,它处于阻塞状态,直到另一个线程调用同一条件上的signalAll方法时为止。
  • 当另一个线程转账时,它应该调用sufficientFunds.signalAll();
  • 这一调用重新激活因为这一条件而等待的所有线程。当这些线程从等待集当中移出时,它们再次成为可运行的,调度器将再次激活它们。同时,它们将试图重新进入该对象。一旦锁成为可用的,它们中的某个将从await调用返回,获得该锁并从被阻塞的方法继续执行。
  • 此时,线程应该再次测试该条件。由于无法确保该条件被满足 —signalAll方法仅仅是通知正在等待的线程:此时有可能已经满足条件,值得再次去检测该条件。
  • 至关重要的是最终需要某个其它线程调用signalAll方法。当一个线程调用await时,它没有办法重新激活自身。它寄希望于其它线程。如果没有其它线程来重新激活等待的线程,它就永远不再运行了。这将导致死锁现象。如果所有其它线程被阻塞,最后一个活动线程在解除其它线程的阻塞状态之前就调用了await方法,那么它也被阻塞。没有任何线程可以解除其它线程的阻塞,那么该程序就挂起。
  • 在对象的状态有利于等待线程的方法改变时调用signalAll。例如,当一个账户余额发生了改变时,等待的线程会应该有机会检测余额。
  • 调用signalAll不会立即激活一个等待线程。它仅仅解除等待线程的阻塞,以便这些线程可以在当前线程退出同步方法之后,通过竞争实现对对象的访问。
  • 另一个方法signal,则是随机解除等待集中某个线程的阻塞状态。这比解除所有线程的阻塞更加有效,但是也存在着危险。如果随机选择的线程发现自己任然不能运行,那么它再次被阻塞。如果没有其它线程再次调用signal,那么系统就死锁了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值