接口 Condition

java.util.concurrent.locks 接口 Condition

所有已知实现类:
AbstractQueuedLongSynchronizer.ConditionObject, AbstractQueuedSynchronizer.ConditionObject

public interface Condition

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

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

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

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

ArrayBlockingQueue 类提供了这项功能,因此没有理由去实现这个示例类。)

Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。

注意,Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 waitnotification 监视器方法。获取 Condition 实例的监视器锁或者使用其监视器方法,与获取和该 Condition 相关的 Lock 或使用其 waitingsignalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 Condition 实例。

除非另行说明,否则为任何参数传递 null 值将导致抛出 NullPointerException

实现注意事项

在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。

因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。

要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。

由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。

从以下版本开始:
1.5

方法摘要
 void await()
          造成当前线程在接到信号或被中断之前一直处于等待状态。
 boolean await(long time, TimeUnit unit)
          造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
 long awaitNanos(long nanosTimeout)
          造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
 void awaitUninterruptibly()
          造成当前线程在接到信号之前一直处于等待状态。
 boolean awaitUntil(Date deadline)
          造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
 void signal()
          唤醒一个等待线程。
 void signalAll()
          唤醒所有等待线程。
方法详细信息

await

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

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

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

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

如果当前线程:

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

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

实现注意事项

假定调用此方法时,当前线程保持了与此 Condition 有关联的锁。这取决于确定是否为这种情况以及不是时,如何对此作出响应的实现。通常,将抛出一个异常(比如 IllegalMonitorStateException)并且该实现必须对此进行记录。

与响应某个信号而返回的普通方法相比,实现可能更喜欢响应某个中断。在这种情况下,实现必须确保信号被重定向到另一个等待线程(如果有的话)。

抛出:
InterruptedException - 如果当前线程被中断(并且支持中断线程挂起)

awaitUninterruptibly

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

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

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

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

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

实现注意事项

假定调用此方法时,当前线程保持了与此 Condition 有关联的锁。这取决于确定是否为这种情况以及不是时,如何对此作出响应的实现。通常,将抛出一个异常(比如 IllegalMonitorStateException)并且该实现必须对此进行记录。


awaitNanos

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

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

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

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

如果当前线程:

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

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

设计注意事项:此方法需要一个 nanosecond 参数,以避免在报告剩余时间时出现截断错误。在发生重新等待时,这种精度损失使得程序员难以确保总的等待时间不少于指定等待时间。

实现注意事项

假定调用此方法时,当前线程保持了与此 Condition 有关联的锁。这取决于确定是否为这种情况以及不是时,如何对此作出响应的实现。通常会抛出一个异常(比如 IllegalMonitorStateException)并且该实现必须对此进行记录。

与响应某个信号而返回的普通方法相比,或者与指示所使用的指定等待时间相比,实现可能更喜欢响应某个中断。在任意一种情况下,实现必须确保信号被重定向到另一个等待线程(如果有的话)。

参数:
nanosTimeout - 等待的最长时间,以毫微秒为单位
返回:
nanosTimeout 值减去花费在等待此方法的返回结果的时间的估算。正值可以用作对此方法进行后续调用的参数,来完成等待所需时间结束。小于等于零的值表示没有剩余时间。
抛出:
InterruptedException - 如果当前线程被中断(并且支持中断线程挂起)

await

boolean await(long time,
              TimeUnit unit)
              throws InterruptedException
造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。此方法在行为上等效于:

   awaitNanos(unit.toNanos(time)) > 0
 
参数:
time - 最长等待时间
unit - time 参数的时间单位
返回:
如果在从此方法返回前检测到等待时间超时,则返回 false,否则返回 true
抛出:
InterruptedException - 如果当前线程被中断(并且支持中断线程挂起)

awaitUntil

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

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

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

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

如果当前线程:

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

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

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

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

实现注意事项

假定调用此方法时,当前线程保持了与此 Condition 有关联的锁。这取决于确定是否为这种情况以及不是时,如何对此作出响应的实现。通常,将抛出一个异常(比如 IllegalMonitorStateException)并且该实现必须对此进行记录。

与响应某个信号而返回的普通方法相比,或者与指示是否到达指定最终期限相比,实现可能更喜欢响应某个中断。在任意一种情况下,实现必须确保信号被重定向到另一个等待线程(如果有的话)。

参数:
deadline - 一直处于等待状态的绝对时间
返回:
如果在返回时已经到达最后期限,则返回 false,否则返回 true
抛出:
InterruptedException - 如果当前线程被中断(并且支持中断线程挂起)

signal

void signal()
唤醒一个等待线程。

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


signalAll

void signalAll()
唤醒所有等待线程。

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

时间: 2024-11-10 07:43:59

接口 Condition的相关文章

Spring5源码分析之启动类的相关接口和注解

一些基础但是核心的知识总结: Spring Boot项目启动的时候需要加@Configuration. @ComponentScan @Configuration  + @Bean  把第三方jar包注入到容器中. 内部的直接 @Service @Controller等等之类配合 @ComponentSscan 的就OK了 @Scope可以实现单例 对于启动默认是饿汉式调用时候创建(但是项目启动时候比较耗费时间),另外一种是调用时候创建 @ComponentScan有排除的用法,排除那个组件 

Java多线程-新特征-锁(上)

在Java5中,专门提供了锁对象,利用锁可以方便的实现资源的封锁,用来控制对竞争资源并发访问的控制,这些内容主要集中在java.util.concurrent.locks 包下面,里面有三个重要的接口Condition.Lock.ReadWriteLock. Condition: Condition 将 Object 监视器方法(wait.notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-se

多线程概念

一个进程是由一个或者N个线程组成的! 线程:cpu调度和分配的基本单位!电脑中真正执行的是线程! 在同一个时间点,我们的电脑只能运行一个线程 多线程: 如果在一个进程中,同时运行多个线程,来完成不同的工作,我们称之为多线程! CUP不能同时运行多个线程! 一个CPU在同一个时间点,只能运行一个线程,单线程运行的速度太快,我们肉眼无法分辨,所以我们认为是多线程! 生活中进入地铁站的例子: 场景1:现在地铁站有1个进站口,同时来了5个人! 需要排队进站! 场景2:现在地铁站有5个进站口,同时来了5个

Java线程:锁

一.锁的原理 Java中每个对象都有一个内置锁,当程序运行到非静态的synchronized同步方法上时,自动获得与正在执行的代码类的当前实例(this实例)有关的锁.获得一个对象的锁也称为获取锁.锁定对象.在对象上锁定或在对象上同步. 当程序运行到synchronized同步方法或代码块时该对象锁才起作用.一个对象只有一个锁.所以一个线程获得该所,就没有其他线程获得,直到第一个线程释放(或返回)锁.这也意味着任何其他线程都不能进入该对象上的synchronized方法或代码块,直到该锁被释放.

多线程之间的通信(等待唤醒机制、Lock 及其它线程的方法)

一.多线程之间的通信. 就是多个线程在操作同一份数据, 但是操作的方法不同. 如: 对于同一个存储块,其中有两个存储位:name   sex, 现有两个线程,一个向其中存放数据,一个打印其中的数据. 为了解决上述问题中的安全问题(在存放线程进行存放操作的时候, 打印线程不能对共有数据进行操作),所以应当对两个线程       操作共有数据的代码部分进行同步(使用synchronized(),来进行同步, 注意 :使用同一个对象作为同步锁. 二.等待唤醒机制. 在上述案例实现过后运行,会发现:打印

java线程详解

Java线程:概念与原理 一.操作系统中线程和进程的概念 现在的操作系统是多任务操作系统.多线程是实现多任务的一种方式. 进程是指一个内存中运行的应用程序,每个进程都有自己独立的一块内存空间,一个进程中可以启动多个线程.比如在Windows系统中,一个运行的exe就是一个进程. 线程是指进程中的一个执行流程,一个进程中可以运行多个线程.比如java.exe进程中可以运行很多线程.线程总是属于某个进程,进程中的多个线程共享进程的内存. “同时”执行是人的感觉,在线程之间实际上轮换执行. 二.Jav

Java---20---多线程:生产者和消费者2(JDK1.5升级版)

此篇文章写的是JDK1.5升级版的生产者和消费者. Java 升级之后的解决方法: Lock java.util.concurrent.locks 接口 Lock 所有已知实现类: ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock public interface Lock Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作.此实现允许更灵活的结构,可

Spring Boot . 3 -- Spring Boot Auto_configuration 是如何实现的?

配置是Spring 框架的重要核心之一,所以Spring 应用能够正常的跑起来肯定是需要配置的,但是使用的Spring Boot 后很多配置没有做,那么AUTO-CONFIGURATION 到底是怎么发生的呢?发生的顺序和步骤是怎样的?怎么实现的呢? 在应用中使用Spring Boot的时候,spring-boot-autoconfigure这个jar包会被引入进来,这个Jar包里面包含了一些配置类.这个包里的所有都在应用的claspath上,都有可能在自动配置执行的过程中被用到,从而简化应用的

浅析线程及同步

守护线程 通常情况下 后台的应用程序就是守护程序 被守护的线程结束了即使守护的线程没有结束那么也将会结束 线程的局部变量:解决多线程共享同个对象的时候,对象属性数据安全问题 ThreadLocal通过get获取 线程池:线程的容器可以帮助我们进行线程的创建.ExecutorService 说明:线程类,创建线程的主要类 Thread currentThread() 获取当前代码所在的线程对象 String getName() 获取线程的名称 主线程名:main 子线程名:Thread-编号,编号