5.5 Condition接口
一、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的监视器主要方法对比
Condition | Object | 作用 |
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 实例。