5.5 Condition接口

2016-06-26 11:39:38 4,064 1


一、Condition的基本介绍

Condition的作用是替代Object的监视器方法监视器方法(wait、notify 和 notifyAll),其主要是与Lock配合使用。

我们知道Object类的wait、notify 和 notifyAll三个方法必须在同步代码块(synchronized)中调用,但是在java并发包中,我们使用了ReentrantLock替代了synchronized关键字,因此我们无法直接调用wait、notify 和 notifyAll。

ReentrantLock lock=new ReentrantLock();
lock.lock();
//....同步执行的代码块之间无法直接使用wait,notify与notifyAll,因为要求必须在同步代码块(synchronized)中使用
lock.unlock();

因此,为了实现这个功能,我们必须要有另外一种替代的机制,这就是Condition的作用。

Condition接口的方法与Object的监视器主要方法对比

ConditionObject作用
await()
wait()造成当前线程在接到信号或被中断之前一直处于等待状态。

await(long time, TimeUnit unit) 

wait(long timeout)造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
signal()notify()唤醒一个等待线程。

signalAll() 

notifyAll()唤醒所有等待线程。

类似于wait、notify 和 notifyAll必须在同步代码块中使用,Condition对象的方法也必须要写在Lock.lock与Lock.unLock()代码之间

ReentrantLock lock=new ReentrantLock();
Condition condition = lock.newCondition();
lock.lock();
/*获取到锁之后才能调用以下方法
condition.await();
condition.signal();
condition.signalAll();*/
lock.unlock();

二、Condition使用案例

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

作为一个示例,假定有一个有界的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 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)
           notFull.await();
         items[putptr] = x;
         if (++putptr == items.length) putptr = 0;
         ++count;
         notEmpty.signal();
       } finally {
         lock.unlock();
       }
     }
  
     public Object take() throws InterruptedException {
       lock.lock();
       try {
         while (count == 0)
           notEmpty.await();
         Object x = items[takeptr];
         if (++takeptr == items.length) takeptr = 0;
         --count;
         notFull.signal();
         return x;
       } finally {
         lock.unlock();
       }
     }
   }

Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。 

注意,Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 wait 和 notification 监视器方法。获取 Condition 实例的监视器锁或者使用其监视器方法,与获取和该 Condition 相关的 Lock 或使用其 waiting 和 signalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 Condition 实例。