多线程(六)Condition

ConditionObject 监视器方法(waitnotifynotifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

条件(也称为条件队列条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。

作为一个示例,假定有一个绑定的缓冲区,它支持 puttake 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待集合中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。

class BoundedBuffer {
   final Lock lock = new ReentrantLock();
   final Condition notFull  = lock.newCondition();     //不满
   final Condition notEmpty = lock.newCondition();     //不为空

   final Object[] items = new Object[100];
   int putptr, takeptr, count;

   public void put(Object x) throws InterruptedException {
     lock.lock();
     try {
       while (count == items.length) //count满了,notFull等待并释放锁,到另一个里面
         notFull.await();
       items[putptr] = x; 
       if (++putptr == items.length) putptr = 0;//尝试是否满了,满了的话,从0开始
       ++count;
       notEmpty.signal();    //通知notEmpty不用等了
     } finally {
       lock.unlock();    //释放锁
     }
   }

   public Object take() throws InterruptedException {
     lock.lock();
     try {
       while (count == 0) //count没有,notEmpty等待并释放锁,到另一个里面
         notEmpty.await();
       Object x = items[takeptr]; 
       if (++takeptr == items.length) takeptr = 0;
       --count;
       notFull.signal();
       return x;
     } finally {
       lock.unlock();
     }
   } 
 }

和Object的wait()一样,在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

public interface Condition {

/*    造成当前线程在接到信号或被中断之前一直处于等待状态。 
    与此 Condition 相关的锁以原子方式释放,并且使当前线程将一直处于休眠状态,直到: 
    (1)其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者 
    (2)其他某个线程调用此 Condition 的 signalAll() 方法;或者 
    (3)其他某个线程中断当前线程,且支持中断线程的挂起;或者 
    (4)发生“虚假唤醒” 
    在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。
    在线程返回时,可以保证 它保持此锁。 
    注意:如果当前线程: 
    (1)在进入此方法时已经设置了该线程的中断状态;或者 
    (2)在支持等待和中断线程挂起时,线程被中断, 
    则抛出 InterruptedException,并清除当前线程的中断状态。
    在第一种情况下,没有指定是否在释放锁之前发生中断测试。 
     */
    void await() throws InterruptedException;

/*    造成当前线程在接到信号之前一直处于等待状态。 
    与此条件相关的锁以原子方式释放,并使当前线程将一直处于休眠状态,直到: 
    (1)其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者 
    (2)其他某个线程调用此 Condition 的 signalAll() 方法;或者 
    (3)发生“虚假唤醒” 
    在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。
    在线程返回时,可以保证 它保持此锁。 
    注意:
    (1)如果在进入此方法时设置了当前线程的中断状态,或者
    (2)在等待时,线程被中断,
    那么在接到信号之前,它将继续等待。当最终从此方法返回时,仍然将设置其中断状态。 
     */
    void awaitUninterruptibly();

/*    造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。 
    与此条件相关的锁以原子方式释放,并且使当前线程将一直处于休眠状态,直到: 
    (1)其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者 
    (2)其他某个线程调用此 Condition 的 signalAll() 方法;或者 
    (3)其他某个线程中断当前线程,且支持中断线程的挂起;或者 
    (4)已超过指定的等待时间;或者 
    (5)发生“虚假唤醒”。 
    在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。
    在线程返回时,可以保证 它保持此锁。 
    注意:如果当前线程: 
    (1)在进入此方法时已经设置了该线程的中断状态;或者 
    (2)在支持等待和中断线程挂起时,线程被中断, 
    则抛出 InterruptedException,并且清除当前线程的已中断状态。
    在第一种情况下,没有指定是否在释放锁之前发生中断测试。 
    在返回时,该方法返回了所剩毫微秒数的一个估计值,以等待所提供的 nanosTimeout 值的时间,
    如果超时,则返回一个小于等于 0 的值。可以用此值来确定在等待返回但某一等待条件仍不具备的情况下,
    是否要再次等待,以及再次等待的时间。此方法的典型用法采用以下形式: 

 synchronized boolean aMethod(long timeout, TimeUnit unit) {
   long nanosTimeout = unit.toNanos(timeout);
   while (!conditionBeingWaitedFor) {
     if (nanosTimeout > 0)
         nanosTimeout = theCondition.awaitNanos(nanosTimeout);
      else
        return false;
   }
   // ... 
 }
     设计注意事项:此方法需要一个 nanosecond 参数,以避免在报告剩余时间时出现截断错误。
    在发生重新等待时,这种精度损失使得程序员难以确保总的等待时间不少于指定等待时间。 
     */
    long awaitNanos(long nanosTimeout) throws InterruptedException;

/*    造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。此方法在行为上等效于:
   awaitNanos(unit.toNanos(time)) > 0
    time - 最长等待时间,unit - time 参数的时间单位 
     */
    boolean await(long time, TimeUnit unit) throws InterruptedException;

/*    造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。 
    与此条件相关的锁以原子方式释放,并且使当前线程将一直处于休眠状态,直到: 
    (1)其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者 
    (2)其他某个线程调用此 Condition 的 signalAll() 方法;或者 
    (3)其他某个线程中断当前线程,且支持中断线程的挂起;或者 
    (4)指定的最后期限到了;或者 
    (5)发生“虚假唤醒”。 
    在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。
    在线程返回时,可以保证 它保持此锁。 
    注意:如果当前线程: 
    (1)在进入此方法时已经设置了该线程的中断状态;或者 
    (2)在支持等待和中断线程挂起时,线程被中断, 
    则抛出 InterruptedException,并且清除当前线程的已中断状态。
    在第一种情况下,没有指定是否在释放锁之前发生中断测试。 
    返回值指示是否到达最后期限,使用方式如下: 

 synchronized boolean aMethod(Date deadline) {
   boolean stillWaiting = true;
   while (!conditionBeingWaitedFor) {
     if (stillWaiting)
         stillWaiting = theCondition.awaitUntil(deadline);
      else
        return false;
   }
   // ... 
 }
参数:deadline - 一直处于等待状态的绝对时间 
返回:如果在返回时已经到达最后期限,则返回 false,否则返回 true 
     */
    boolean awaitUntil(Date deadline) throws InterruptedException;

/*    唤醒一个等待线程。 
    如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁。
     */
    void signal();

/*    唤醒所有等待线程。 
    如果所有的线程都在等待此条件,则唤醒所有线程。在从 await 返回之前,每个线程都必须重新获取锁。 
     */
    void signalAll();
}

猜你喜欢

转载自blog.csdn.net/xiao1_1bing/article/details/81349433