Java Concurrency - Condition

时间:2023-03-09 04:42:44
Java Concurrency - Condition

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

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

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

作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 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)
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 实例。

Condition 的方法

await()

造成当前线程在接到信号或被中断之前一直处于等待状态。

与此 Condition 相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下四种情况之一 以前,当前线程将一直处于休眠状态:

  • 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者
  • 其他某个线程调用此 Condition 的 signalAll() 方法;或者
  • 其他某个线程中断当前线程,且支持中断线程的挂起;或者
  • 发生 “虚假唤醒”

在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。在线程返回时,可以保证它保持此锁。

如果当前线程:

  • 在进入此方法时已经设置了该线程的中断状态;或者
  • 在支持等待和中断线程挂起时,线程被中断,

则抛出 InterruptedException,并清除当前线程的中断状态。在第一种情况下,没有指定是否在释放锁之前发生中断测试。

awaitNanos(long nanosTimeout)

造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。

与此条件相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下五种情况之一以前,当前线程将一直处于休眠状态:

  • 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者
  • 其他某个线程调用此 Condition 的 signalAll() 方法;或者
  • 其他某个线程中断当前线程,且支持中断线程的挂起;或者
  • 已超过指定的等待时间;或者
  • 发生 “虚假唤醒”。

在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。在线程返回时,可以保证它保持此锁。

如果当前线程:

  • 在进入此方法时已经设置了该线程的中断状态;或者
  • 在支持等待和中断线程挂起时,线程被中断,

则抛出 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;
}
// ...
}

await(long time, TimeUnit unit)

造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。此方法在行为上等效于:

awaitNanos(unit.toNanos(time)) > 0

awaitUntil(Date deadline)

造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。

与此条件相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下五种情况之一以前,当前线程将一直处于休眠状态:

  • 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者
  • 其他某个线程调用此 Condition 的 signalAll() 方法;或者
  • 其他某个线程中断当前线程,且支持中断线程的挂起;或者
  • 指定的最后期限到了;或者
  • 发生 “虚假唤醒”。

在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。在线程返回时,可以保证 它保持此锁。

如果当前线程:

  • 在进入此方法时已经设置了该线程的中断状态;或者
  • 在支持等待和中断线程挂起时,线程被中断,

则抛出 InterruptedException,并且清除当前线程的已中断状态。在第一种情况下,没有指定是否在释放锁之前发生中断测试。

返回值指示是否到达最后期限,使用方式如下:

synchronized boolean aMethod(Date deadline) {
boolean stillWaiting = true;
while (!conditionBeingWaitedFor) {
if (stillWaiting)
stillWaiting = theCondition.awaitUntil(deadline);
else
return false;
}
// ...
}

awaitUninterruptibly()

造成当前线程在接到信号之前一直处于等待状态。

与此条件相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下三种情况之一以前,当前线程将一直处于休眠状态:

  • 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者
  • 其他某个线程调用此 Condition 的 signalAll() 方法;或者
  • 发生“虚假唤醒”

在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。在线程返回时,可以保证它保持此锁。

如果在进入此方法时设置了当前线程的中断状态,或者在等待时线程被中断,那么在接到信号之前,它将继续等待。当最终从此方法返回时,仍然将设置其中断状态。

signal()

唤醒一个等待线程。

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

signalAll()

唤醒所有等待线程。

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