类说明
- Condition将Object监视器方法(wait、notify和notifyAll)分解成独立的对象,通过与任意Lock实现结合使用,从而达到每个对象拥有多个等待集(wait-sets per object)的效果。当Lock替换synchronized方法和语句的使用时,Condition则替换了Object监视器方法的使用。
- Conditions (也称为“条件队列(condition queues)”或“条件变量(condition variables)”)提供了一种机制,使得一个线程可以暂停执行(即“等待”),直到另一个线程通知某些状态条件现在可能为真。因为对这种共享状态信息的访问发生在不同的线程中,所以必须对其进行保护,因此某种形式的锁会与条件相关联。等待条件的关键特性是它能够原子性地(atomically)释放关联的锁并挂起当前线程,这类似于Object.wait。
- Condition实例本质上是与一个锁绑定的。要为特定的Lock实例获取一个Condition实例,请使用该Lock实例的newCondition()方法. 做为一个例子,假设我们有一个支持put和take方法的有界缓冲区。如果在一个空缓冲区上尝试take操作,那么线程将会阻塞,直到有可用的项目;如果在一个满缓冲区上尝试put操作,那么线程将会阻塞,直到有可用的空间。我们希望将等待的put线程和take线程放在不同的等待集(wait-sets)中,这样就可以在缓冲区中有项目或空间可用时,只通知一个线程,从而实现优化。这可以通过使用两个Condition实例来实现,代码如下:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class BoundedBuffer<E> {
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(E 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 E take() throws InterruptedException {
lock.lock();
try {
while (count == 0) {
notEmpty.await();
}
E x = (E) items[takeptr];
if (++takeptr == items.length) {
takeptr = 0;
}
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
( java.util.concurrent.ArrayBlockingQueue类提供了当前函数的功能,没有理由再去实现这个样例类)
4. Condition 接口的实现可以提供与 Object 监视器方法不同的行为和语义,例如保证通知的顺序,或者在执行通知时不需要持有锁。如果某个实现提供了这种特殊化的语义,那么该实现必须对这些语义进行文档说明。
5. 请注意,Condition 实例只是普通的对象,它们可以作为 synchronized 语句的目标,并且可以调用它们自己的监视器方法,如 Object#wait 和 Object#notify。获取 Condition 实例的监视器锁或使用其监视器方法,与获取与该 Condition 关联的 Lock 或使用其 await 和 signal 方法之间没有指定的关系。为了避免混淆,建议你永远不要以这种方式使用 Condition 实例,除非可能是在它们自己的实现中。
6. 除非另有说明,对任何参数传递null值都将导致抛出 NullPointerException
实现考虑(Implementation Considerations)
- 当等待在一个
Condition
时,允许发生所谓的“虚假唤醒(“spurious wakeup” )”,这通常是由于底层平台语义所做出的妥协。这对大多数应用程序来说几乎没有实际影响,因为等待Condition
时总是应该在一个循环中进行,并测试正在等待的状态谓词。实现可以自由地消除虚假唤醒的可能性,但建议应用程序员始终假设它们可能会发生,并因此总是在循环中等待。 - 条件等待(condition waiting)的三种形式(可中断、不可中断和定时)在某些平台上可能在实现的难易程度和性能特征上有所不同。特别是,提供这些功能并保持特定的语义(如顺序保证)可能是困难的。此外,在所有平台上实现对线程实际挂起的中断可能并不总是可行的。
- 因此,实现不要求为所有三种等待形式定义完全相同的保证或语义,也不要求支持线程挂起的实际中断。 实现必须清楚地记录每种等待方法提供的语义和保证。如果某个实现确实支持线程挂起的中断,则它必须遵守此接口中定义的中断语义。
- 实现必须清楚地记录每种等待方法提供的语义和保证。如果某个实现确实支持线程挂起的中断,则它必须遵守此接口中定义的中断语义。
- 由于中断通常意味着取消,并且检查中断的情况通常很少见,实现可以选择优先响应中断而不是正常的方法返回。即使可以证明中断发生在另一个可能已经解除线程阻塞的动作之后也是如此。实现应该记录这种行为。
public interface Condition {
/**
* 造成线程等待,直到signalled 或者interrupted
*
* 与这个条件关联的锁会被原子性地释放,并且当前线程会变得不可调度,进入休眠状态,直到以下四种情况之一发生:
* 1. 其他某个线程对这个Condition调用了signal方法,并且当前线程恰好被选中为要唤醒的线程
* 2. 其他某个线程对这个Condition调用了signalAll方法
* 3. 其他某个线程中断了当前线程,并且支持在挂起期间中断线程
* 4. 发生了虚假唤醒。
* 在所有情况下,这个方法返回之前,当前线程必须重新获取与此条件关联的锁。当线程返回时,保证它持有这把锁
*
* 如果当前线程:
* 1. 有自己的interrupted 状态设置进入这个方法
* 2. 是interrupted在等待的时候,并且支持在挂起期间中断线程
* 那么:
* 抛出InterruptedException,当前线程的interrupted 状态被清除
* 如果没有特别说明,在第一种情况下,并未指定中断检测是在释放锁之前还是之后进行
*
* 实现考虑:
* 1. 当调用这个方法时,假设当前线程持有与此Condition关联的锁。由实现者来决定这种情况是否成立,
* 如果不成立,应该如何响应。
* 通常情况下,会抛出一个异常(例如IllegalMonitorStateException),并且实现者必须在文档中说明这一点。
*
* 2. 实现可以选择优先响应中断,而不是正常的方法返回(响应信号)或指示指定的截止时间已过。
* 无论哪种情况,实现都必须确保信号被重定向到另一个等待的线程(如果有其他等待的线程)。
*
* @throws 如果当前线程被中断(并且支持中断线程挂起),则抛出InterruptedException。
*/
void await() throws InterruptedException;
/**
* 造成线程等待,直到signalled
* 与这个条件关联的锁会被原子性地释放,并且当前线程会变得不可调度,进入休眠状态,直到以下四种情况之一发生:
* 1. 其他某个线程对这个Condition调用了signal方法,并且当前线程恰好被选中为要唤醒的线程
* 2. 其他某个线程对这个Condition调用了signalAll方法
* 3. 发生了虚假唤醒。
* 在所有情况下,这个方法返回之前,当前线程必须重新获取与此条件关联的锁。当线程返回时,保证它持有这把锁
*
* 如果当前线程在进入此方法时中断状态已被设置,或者在等待过程中被中断(例如通过调用 Thread#interrupt),它将继续等待直到被唤醒。
* 当它最终从这个方法返回时,它的中断状态仍然会被设置。
*
* 实现考虑:
* 当调用这个方法时,假设当前线程持有与此Condition关联的锁。由实现者来决定这种情况是否成立,
* 如果不成立,应该如何响应。
* 通常情况下,会抛出一个异常(例如IllegalMonitorStateException),并且实现者必须在文档中说明这一点。
*/
void awaitUninterruptibly();
/**
* 造成线程等待,直到signalled 或者interrupted 或者过了指定的deadline
*
* 与这个条件关联的锁会被原子性地释放,并且当前线程会变得不可调度,进入休眠状态,直到以下五种情况之一发生:
* 1. 其他某个线程对这个Condition调用了signal方法,并且当前线程恰好被选中为要唤醒的线程
* 2. 其他某个线程对这个Condition调用了signalAll方法
* 3. 其他某个线程中断了当前线程,并且支持在挂起期间中断线程
* 4. 指定的截止时间已过。
* 5. 发生了虚假唤醒。
* 在所有情况下,这个方法返回之前,当前线程必须重新获取与此条件关联的锁。当线程返回时,保证它持有这把锁
* 如果当前线程:
* 1. 有自己的interrupted 状态设置进入这个方法
* 2. 是interrupted在等待的时候,并且支持在挂起期间中断线程
* 那么:
* 抛出InterruptedException,当前线程的interrupted 状态被清除
* 如果没有特别说明,在第一种情况下,并未指定中断检测是在释放锁之前还是之后进行
*
* boolean aMethod(long timeout, TimeUnit unit)
* throws InterruptedException {
* long nanosRemaining = unit.toNanos(timeout);
* lock.lock();
* try {
* while (!conditionBeingWaitedFor()) {
* if (nanosRemaining <= 0L)
* return false;
* nanosRemaining = theCondition.awaitNanos(nanosRemaining);
* }
* // ...
* return true;
* } finally {
* lock.unlock();
* }
* }}
*
* 设计说明:此方法需要一个纳秒参数,以避免在报告剩余时间时发生截断误差。
* 这种精度损失将使得程序员难以确保在重新等待时,总的等待时间不会系统性地短于指定的时间。
*
* 实现考虑:
* 1. 当调用这个方法时,假设当前线程持有与此Condition关联的锁。由实现者来决定这种情况是否成立,
* 如果不成立,应该如何响应。
* 通常情况下,会抛出一个异常(例如IllegalMonitorStateException),并且实现者必须在文档中说明这一点。
*
* 2. 实现可以选择优先响应中断,而不是正常的方法返回(响应信号)或指示指定的截止时间已过。
* 无论哪种情况,实现都必须确保信号被重定向到另一个等待的线程(如果有其他等待的线程)。
*
* @param nanosTimeout 最大等待时间, 纳秒为单位
* @return 对 nanosTimeout值减去从该方法返回时所花费的等待时间的一个估计值,
* 一个正值可以作为参数用于对该方法的后续调用,以完成剩余的等待时间
* 如果值小于或等于零,则表示没有剩余的等待时间
* @throws 如果当前线程被中断(并且支持中断线程挂起),则抛出InterruptedException。
*/
long awaitNanos(long nanosTimeout) throws InterruptedException;
/**
* 造成线程等待,直到signalled 或者interrupted 或者过了指定的deadline
* awaitNanos(unit. toNanos(time)) > 0
*
* @param time 最大等待时间
* @param unit 等待时间的单位
* @return 如果在方法返回之前明显地检测到等待时间已经过去,则返回 {@code false},否则返回 {@code true}。
* @throws 如果当前线程被中断(并且支持中断线程挂起),则抛出InterruptedException。
*/
boolean await(long time, TimeUnit unit) throws InterruptedException;
/**
*
* 造成线程等待,直到signalled 或者interrupted 或者过了指定的deadline
*
* 与这个条件关联的锁会被原子性地释放,并且当前线程会变得不可调度,进入休眠状态,直到以下五种情况之一发生:
* 1. 其他某个线程对这个Condition调用了signal方法,并且当前线程恰好被选中为要唤醒的线程
* 2. 其他某个线程对这个Condition调用了signalAll方法
* 3. 其他某个线程中断了当前线程,并且支持在挂起期间中断线程
* 4. 指定的截止时间已过。
* 5. 发生了虚假唤醒。
* 在所有情况下,这个方法返回之前,当前线程必须重新获取与此条件关联的锁。当线程返回时,保证它持有这把锁
* 如果当前线程:
* 1. 有自己的interrupted 状态设置进入这个方法
* 2. 是interrupted在等待的时候,并且支持在挂起期间中断线程
* 那么:
* 抛出InterruptedException,当前线程的interrupted 状态被清除
* 如果没有特别说明,在第一种情况下,并未指定中断检测是在释放锁之前还是之后进行
*
* 返回值指示截止时间是否已经过去,可以按如下方式使用:
* ` boolean aMethod(Date deadline) throws InterruptedException {
* boolean stillWaiting = true;
* lock. lock();
* try { while (!conditionBeingWaitedFor()) {
* if (!stillWaiting)
* return false;
* stillWaiting = theCondition. awaitUntil(deadline);
* } // ... return true; }
* finally {
* lock. unlock(); }
* }`
*
* 实现考虑:
* 1. 当调用这个方法时,假设当前线程持有与此Condition关联的锁。由实现者来决定这种情况是否成立,
* 如果不成立,应该如何响应。
* 通常情况下,会抛出一个异常(例如IllegalMonitorStateException),并且实现者必须在文档中说明这一点。
* 2. 实现可以选择优先响应中断,而不是正常的方法返回(响应信号)或指示指定的截止时间已过。
* 无论哪种情况,实现都必须确保信号被重定向到另一个等待的线程(如果有其他等待的线程)。
*
* @param deadline 一个线程等待直到某个特定的时间点
* @return 如果在返回时截止时间已经过去,则返回false。否则,返回true。
* @throws InterruptedException 如果当前线程被中断(并且支持中断线程挂起),则抛出InterruptedException。
*/
boolean awaitUntil(Date deadline) throws InterruptedException;
/**
* 唤醒一个正在等待的线程
* 1. 如果有任何线程正在等待这个条件,则选择其中一个进行唤醒。该线程在从`await`方法返回之前必须重新获取锁。
* 实现考虑:
* 实现可能(通常也是这样)要求当前线程在调用此方法时持有与此`Condition`关联的锁。
* 实现必须记录这一前提条件以及如果未持有锁时采取的任何操作。通常情况下,会抛出如`IllegalMonitorStateException`这样的异常
*/
void signal();
/**
* 唤醒所有正在等待的线程。
* 1. 如果有任何线程正在等待这个条件,则它们将全部被唤醒。每个线程在从`await`方法返回之前必须重新获取锁。
* 实现考虑:
* 实现可能(通常也是这样)要求当前线程在调用此方法时持有与此`Condition`关联的锁。
* 实现必须记录这一前提条件以及如果未持有锁时采取的任何操作。通常情况下,会抛出如`IllegalMonitorStateException`这样的异常
*/
void signalAll();
}