今天是:
带着程序的旅程,每一行代码都是你前进的一步,每个错误都是你成长的机会,最终,你将抵达你的目的地。
title

Condition

概述

条件将对象监视器方法(等待、通知和通知全部)分解为不同的对象,通过将它们与任意 Lock 实现相结合,为每个对象提供多个等待集的效果。Lock 取代了同步方法和语句的使用,条件取代了对象监视器方法的使用。条件(也称为条件队列或条件变量)为一个线程提供了一种暂停执行(“等待”)的方法,直到另一个线程通知某个状态条件现在可能为真。由于对此共享状态信息的访问发生在不同的线程中,因此必须对其进行保护,因此某种形式的锁与条件相关联。等待条件提供的关键属性是它以原子方式释放关联的锁并挂起当前线程,就像 Object.wait 一样。条件实例本质上绑定到锁。要获取特定 Lock 实例的条件实例,请使用其 newCondition() 方法。例如,假设我们有一个支持放置和获取方法的有界缓冲区。如果尝试在空缓冲区上获取,则线程将阻塞,直到项目可用;如果在已满缓冲区上尝试放置,则线程将阻塞,直到有空间可用。我们希望继续等待放置线程并将线程放在单独的等待集中,以便我们可以使用在缓冲区中可用项目或空间时仅通知单个线程的优化。这可以使用两个条件实例来实现。

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 类提供了此功能,因此没有理由实现此示例用法类。条件实现可以提供不同于对象监视器方法的行为和语义,例如保证通知的顺序,或者在执行通知时不需要保持锁定。如果实现提供了这样的专用语义,则实现必须记录这些语义。请注意,Condition 实例只是普通对象,它们本身可以用作同步语句中的目标,并且可以调用自己的监视器等待和通知方法。获取条件实例的监视器锁或使用其监视方法与获取与该条件关联的锁或使用其等待和信号方法没有指定的关系。为避免混淆,建议您永远不要以这种方式使用 Condition 实例,除非在它们自己的实现中。除非另有说明,否则为任何参数传递 null 值都将导致引发 NullPointerException。

实现注意事项

在等待条件时,通常允许发生“虚假唤醒”,作为对基础平台语义的让步。这对大多数应用程序几乎没有实际影响,因为条件应始终在循环中等待,测试正在等待的状态谓词。实现可以自由地消除虚假唤醒的可能性,但建议应用程序程序员始终假设它们可能发生,因此始终在循环中等待。三种形式的条件等待(可中断、不可中断和定时)在某些平台上的实现难易程度和性能特征方面可能有所不同。特别是,可能难以提供这些功能并维护特定的语义(如订购保证)。此外,中断线程实际挂起的能力可能并不总是在所有平台上实现的。因此,实现不需要为所有三种形式的等待定义完全相同的保证或语义,也不需要支持线程实际挂起的中断。实现需要清楚地记录每个等待方法提供的语义和保证,当实现确实支持线程挂起中断时,它必须遵守此接口中定义的中断语义。由于中断通常意味着取消,并且中断检查通常不频繁,因此实现可能倾向于响应中断而不是正常方法返回。即使可以证明中断发生在可能已取消阻止线程的另一个操作之后,也是如此。实现应记录此行为。

方法说明

  • await 使当前线程等待,直到发出信号或中断。与此条件关联的锁以原子方式释放,当前线程因线程调度目的而被禁用并处于休眠状态,直到发生以下四种情况之一:其他线程调用此条件的信号方法,并且当前线程恰好被选为要唤醒的线程;或某个其他线程为此条件调用 signalAll 方法;或者其他一些线程中断当前线程,支持线程挂起中断;或发生“虚假唤醒”。在所有情况下,在此方法可以返回之前,当前线程必须重新获取与此条件关联的锁。当线程返回时,它保证保持此锁。如果当前线程:在进入此方法时设置了中断状态;或者在等待时中断,支持线程挂起中断,则抛出中断异常,清除当前线程的中断状态。在第一种情况下,没有指定是否在释放锁之前进行中断测试。实现注意事项  调用此方法时,假定当前线程持有与此条件关联的锁。由实施来确定是否是这种情况,如果不是,如何响应。通常,将引发异常(例如 IllegalMonitorStateException),并且实现必须记录该事实。实现可以倾向于响应中断,而不是响应信号的正常方法返回。在这种情况下,实现必须确保将信号重定向到另一个等待线程(如果有)。
  • awaitUninterruptibly()

    使当前线程等待,直到发出信号。与此条件关联的锁以原子方式释放,当前线程出于线程调度目的而被禁用并处于休眠状态,直到发生以下三种情况之一:其他线程为此条件调用信号方法,并且当前线程恰好被选为要唤醒的线程;或某个其他线程为此条件调用 signalAll 方法;或发生“虚假唤醒”。在所有情况下,在此方法可以返回之前,当前线程必须重新获取与此条件关联的锁。当线程返回时,它保证保持此锁。如果当前线程在进入此方法时设置了中断状态,或者在等待时中断,则将继续等待,直到发出信号。当它最终从此方法返回时,仍将设置其中断状态。实现注意事项 调用此方法时,假定当前线程持有与此条件关联的锁。由实现来确定是否是这种情况,如果不是,如何响应。通常,将引发异常(例如 IllegalMonitorStateException),并且实现必须记录该事实。

  • awaitNanos(long nanosTimeout)

    该方法在返回时给定提供的 nanosTimeout 值的情况下返回剩余等待的纳秒数的估计值,如果超时,则返回小于或等于零的值。此值可用于确定在等待返回但等待条件仍未成立的情况下是否重新等待以及重新等待多长时间。此方法的典型用法采用以下形式

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();
   }
 }

设计说明:此方法需要纳秒参数,以避免在报告剩余时间时出现截断错误。这种精度损失将使程序员难以确保总等待时间不会系统地短于发生重新等待时指定的时间。实现注意事项 调用此方法时,假定当前线程持有与此条件关联的锁。由实施来确定是否是这种情况,如果不是,如何响应。通常,将引发异常(例如 IllegalMonitorStateException),并且实现必须记录该事实。实现可以倾向于响应中断,而不是响应信号的正常方法返回,或者指示指定等待时间的经过。在任何一种情况下,实现都必须确保将信号重定向到另一个等待线程(如果有)。参数:nanosTimeout – 等待的最长时间,以纳秒为单位 返回:估计 nanosTimeout 值减去从此方法返回时等待所花费的时间。正值可用作对此方法的后续调用的参数,以完成等待所需时间。小于或等于零的值表示没有剩余时间。

  • await(long time, TimeUnit unit)

    使当前线程等待,直到发出信号或中断,或者指定的等待时间过去。此方法在行为上等效于:awaitNanos(unit.toNanos(time)) > 0

  • awaitUntil(Date deadline)

    在等待时间之内可以被其它线程唤醒,等待时间一过该线程会自动唤醒,和别的线程争抢锁资源

  • void signal()

    唤醒一个等待线程。如果有任何线程正在等待此条件,则选择一个线程进行唤醒。然后,该线程必须在从 await 返回之前重新获取锁。实现注意事项 在调用此方法时,实现可能(并且通常确实)要求当前线程持有与此条件关联的锁。实现必须记录此前提条件以及未保持锁时采取的任何操作。通常,将引发异常,例如 IllegalMonitorStateException。

  • void signalAll()

    唤醒所有等待的线程。如果有任何线程正在等待这种情况,则它们都将被唤醒。每个线程必须重新获取锁,然后才能从 await 返回。

分享到:

专栏

类型标签

网站访问总量