https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html
Condition
java.util.concurrent.locks
public interface Condition
Condition 将对象监视器方法(wait、notify 和 notifyAll)分解为不同的对象,通过将它们与任意 Lock 实现的使用结合起来,使每个对象具有多个等待集的效果。 Lock 代替了同步方法和语句的使用,Condition 代替了 Object 监视器方法的使用。
条件(也称为条件队列或条件变量)为一个线程提供了一种暂停执行(“等待”)的方法,直到另一个线程通知某个状态条件现在可能为真。因为对这种共享状态信息的访问发生在不同的线程中,所以它必须受到保护,所以某种形式的锁与条件相关联。等待条件提供的关键属性是它以原子方式释放关联的锁并挂起当前线程,就像 Object.wait 一样。
Condition 实例本质上绑定到锁。要获取特定 Lock 实例的 Condition 实例,请使用其 newCondition() 方法。
例如,假设我们有一个支持 put 和 take 方法的有界缓冲区。如果在空缓冲区上尝试获取,则线程将阻塞,直到项目变得可用;如果在一个完整的缓冲区上尝试放置,则线程将阻塞,直到有空间可用。我们希望继续等待 put 线程并在单独的等待集中获取线程,以便我们可以使用在缓冲区中可用的项目或空间时仅通知单个线程的优化。这可以使用两个 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();
}
}
}
(ArrayBlockingQueue 类提供了这个功能,所以没有理由实现这个示例使用类。)
条件实现可以提供与对象监视器方法不同的行为和语义,例如保证通知的顺序,或者在执行通知时不需要持有锁。如果实现提供了这种专门的语义,那么实现必须记录这些语义。
请注意,Condition 实例只是普通对象,它们本身可以用作同步语句中的目标,并且可以调用它们自己的监视器等待和通知方法。获取 Condition 实例的监视器锁,或使用其监视器方法,与获取与该 Condition 关联的锁或使用其等待和信令方法没有指定关系。为避免混淆,建议不要以这种方式使用 Condition 实例,除非在它们自己的实现中。
除非另有说明,否则为任何参数传递 null 值将导致引发 NullPointerException。
实现注意事项
在等待条件时,通常允许发生“虚假唤醒”,作为对底层平台语义的让步。这对大多数应用程序几乎没有实际影响,因为应始终在循环中等待条件,测试正在等待的状态谓词。一个实现可以自由地消除虚假唤醒的可能性,但建议应用程序程序员总是假设它们可以发生,因此总是在循环中等待。
条件等待的三种形式(可中断、不可中断和定时)在某些平台上实现的难易程度和性能特征可能不同。特别是,可能难以提供这些功能并维护特定的语义,例如排序保证。此外,中断线程的实际挂起的能力可能并不总是适用于所有平台。
因此,实现不需要为所有三种等待形式定义完全相同的保证或语义,也不需要支持线程实际挂起的中断。
实现需要清楚地记录每个等待方法提供的语义和保证,并且当实现确实支持线程挂起的中断时,它必须遵守此接口中定义的中断语义。
由于中断通常意味着取消,并且对中断的检查通常不常见,因此实现可以倾向于响应中断而不是正常的方法返回。即使可以证明中断发生在另一个可能已经解除阻塞线程的操作之后也是如此。一个实现应该记录这个行为。
Method Detail
void await() throws InterruptedException
使当前线程等待,直到它发出信号或中断。
与此 Condition 关联的锁被自动释放,并且当前线程出于线程调度目的而被禁用并处于休眠状态,直到发生以下四种情况之一:
其他一些线程为此 Condition 调用 signal() 方法,当前线程恰好被选为要唤醒的线程;或者
其他一些线程为此条件调用 signalAll() 方法;或者
其他一些线程中断当前线程,支持中断线程挂起;或者
发生“虚假唤醒”。
在所有情况下,在此方法可以返回之前,当前线程必须重新获取与此条件关联的锁。当线程返回时,它保证持有这个锁。
如果当前线程:
在进入此方法时设置其中断状态;或者
等待时被中断,支持线程挂起的中断,
然后抛出 InterruptedException 并清除当前线程的中断状态。在第一种情况下,没有规定是否在释放锁之前进行中断测试。
实现注意事项
调用此方法时,假定当前线程持有与此 Condition 关联的锁。由实现决定是否是这种情况,如果不是,如何响应。通常,将引发异常(例如 IllegalMonitorStateException),并且实现必须记录该事实。
与响应信号的正常方法返回相比,实现更倾向于响应中断。在这种情况下,实现必须确保将信号重定向到另一个等待线程(如果有的话)。
void awaitUninterruptibly()
导致当前线程等待,直到它发出信号。
与此条件关联的锁被自动释放,当前线程出于线程调度目的而被禁用并处于休眠状态,直到发生以下三种情况之一:
其他一些线程为此 Condition 调用 signal() 方法,当前线程恰好被选为要唤醒的线程;或者
其他一些线程为此条件调用 signalAll() 方法;或者
发生“虚假唤醒”。
在所有情况下,在此方法可以返回之前,当前线程必须重新获取与此条件关联的锁。当线程返回时,它保证持有这个锁。
如果当前线程进入该方法时设置了中断状态,或者在等待中被中断,则继续等待直到signalled。当它最终从这个方法返回时,它的中断状态仍然会被设置。
实现注意事项
调用此方法时,假定当前线程持有与此 Condition 关联的锁。由实现决定是否是这种情况,如果不是,如何响应。通常,将引发异常(例如 IllegalMonitorStateException),并且实现必须记录该事实。
long awaitNanos(long nanosTimeout) throws InterruptedException
boolean await(long time, TimeUnit unit) throws InterruptedException
boolean awaitUntil(Date deadline) throws InterruptedException
void signal()
唤醒一个等待线程。
如果有任何线程在此条件下等待,则选择一个用于唤醒。 然后,该线程必须在从等待返回之前重新获取锁。
实现注意事项
当调用此方法时,实现可能(并且通常确实)要求当前线程持有与此条件关联的锁。 实现必须记录此前提条件以及未持有锁定时采取的任何操作。 通常,会抛出 IllegalMonitorStateException 等异常。
void signalAll()
唤醒所有等待的线程。
如果有任何线程正在等待这种情况,那么它们都会被唤醒。 每个线程必须重新获取锁才能从等待返回。
实现注意事项
当调用此方法时,实现可能(并且通常确实)要求当前线程持有与此条件关联的锁。 实现必须记录此前提条件以及未持有锁定时采取的任何操作。 通常,会抛出 IllegalMonitorStateException 等异常。
网友评论