前言

本文隶属于专栏《100个问题搞定Java并发》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!

本专栏目录结构和参考文献请见100个问题搞定Java并发

正文

如果大家理解了 Object.wait() 方法和 Object.notify() 方法,就能很容易地理解 Condition 对象了。

它与 wait() 方法和 notify() 方法的作用是大致相同的。

关于 wait 和 notify 请参考我的博客——结合JDK源码图文详解 wait 和 notify 的工作原理

但是 wait() 方法和 notify() 方法是与 synchronized 关键字合作使用的,而 Condition 是与重入锁相关联的。

通过 lock 接口(ReentrantLock就实现了这一接口)的 Condition newCondition 方法可以生成一个与当前重入锁绑定的 Condition 实例。

利用 Condition 对象,我们就可以让线程在合适的时间等待,或者在某一个特定的时刻得到通知,继续执行。

源码(JDK8)

/*** Condition 移出 Object 的 monitor 方法( wait , notify和notifyAll )成不同的对象,以得到每个对象具有多个等待集,通过将它们与任意的 Lock 实现混用。 * * Lock替换synchronized方法和语句的使用, Condition取代了对象监视器方法的使用。** 条件(也称为条件队列或条件变量 )为一个线程暂停执行(“wait”)提供了一种方法,直到另一个线程通知某些状态现在可能为真。 * * 因为访问此共享状态信息发生在不同的线程中,所以它必须被保护,因此某种形式的锁与该条件相关联。 * * 等待条件的关键属性是它原子地释放相关的锁并挂起当前线程,就像 Object.wait 。* * 一个Condition实例本质上绑定到一个锁。 要获得特定Condition实例的Condition实例,请使用其newCondition()方法。* * 例如,假设我们有一个有限的缓冲区,它支持put和take方法。 * * 如果在一个空的缓冲区尝试一个take ,则线程将阻塞直到一个项目可用; * * 如果put试图在一个完整的缓冲区,那么线程将阻塞,直到空间变得可用。 * * 我们希望在单独的等待集中等待put线程和take线程,以便我们可以在缓冲区中的项目或空间可用的时候使用仅通知单个线程的优化。 * * 这可以使用两个Condition实例来实现。* * class BoundedBuffer {*   <b>final Lock lock = new ReentrantLock();</b>*   final Condition notFull  = <b>lock.newCondition(); </b>*   final Condition notEmpty = <b>lock.newCondition(); </b>**   final Object[] items = new Object[100];*   int putptr, takeptr, count;**   public void put(Object x) throws InterruptedException {*     <b>lock.lock();*     try {</b>*       while (count == items.length)*         <b>notFull.await();</b>*       items[putptr] = x;*       if (++putptr == items.length) putptr = 0;*       ++count;*       <b>notEmpty.signal();</b>*     <b>} finally {*       lock.unlock();*     }</b>*   }**   public Object take() throws InterruptedException {*     <b>lock.lock();*     try {</b>*       while (count == 0)*         <b>notEmpty.await();</b>*       Object x = items[takeptr];*       if (++takeptr == items.length) takeptr = 0;*       --count;*       <b>notFull.signal();</b>*       return x;*     <b>} finally {*       lock.unlock();*     }</b>*   }* }* * ( ArrayBlockingQueue类提供此功能,因此没有理由实现此示例使用类。)* * Condition实现可以提供Object监视器方法的行为和语义,例如有保证的通知顺序,或者在执行通知时不需要锁定。 * * 如果一个实现提供了这样的专门的语义,那么实现必须记录这些语义。* * 需要注意的是Condition实例只是普通的对象,其本身作为一个目标synchronized语句,自己也可以调用 wait 和 notify 方法。 * * 获取Condition实例的监视器锁或使用其监视方法与获取与该Condition相关联的Condition或使用其wait和signal方法。 * * 建议为避免混淆,您永远不会以这种方式使用Condition实例,除了可能在自己的实现之内。* * 除非另有说明,传递任何参数的null值将导致NullPointerException被抛出。* * 实施注意事项* * 当等待Condition时,允许发生“ 虚假唤醒 ”,一般来说,作为对底层平台语义的让步。 * * 这对大多数应用程序几乎没有实际的影响,因为Condition应该始终在循环中等待,测试正在等待的状态谓词。 * * 一个实现可以免除虚假唤醒的可能性,但建议应用程序员总是假定它们可以发生,因此总是等待循环。* * 条件等待(可中断,不可中断和定时)的三种形式在一些平台上的易用性和性能特征可能不同。 * * 特别地,可能难以提供这些特征并保持特定的语义,例如排序保证。 此外,中断线程实际挂起的能力可能并不总是在所有平台上实现。* * 因此,不需要一个实现来为所有三种形式的等待定义完全相同的保证或语义,也不需要支持中断线程的实际暂停。* * 需要一个实现来清楚地记录每个等待方法提供的语义和保证,并且当一个实现确实支持线程挂起中断时,它必须遵守该接口中定义的中断语义。* * 由于中断通常意味着取消,并且检查中断通常是不频繁的,所以实现可以有利于通过正常方法返回来响应中断。 * * 即使可以显示中断发生在另一个可能解除阻塞线程的动作之后,这一点也是如此。 一个实现应该记录这个行为。* * @since 1.5* @author Doug Lea*/public interface Condition {/*** 导致当前线程等到发信号或interrupted 。* * 与此 Condition 关联的 Lock 是原子方式释放,当前线程将禁用线程调度并且休眠直到下面三件事中的一件发生:* * 一些其它线程调用本Condition的signal()方法,当前线程碰巧被选择作为被唤醒线程;* * 一些其他的线程调用本Condition的signalAll();* * 发生了“虚假唤醒 ”。* * 在所有情况下,在此方法返回之前,当前线程必须重新获取与此条件相关的锁。 * * 当线程返回时,它保证保持此锁。* * 如果当前线程:* * 在进入该方法时设置了中断状态; * * 或者在 wait 的时候被 Thread#interrupt 中断,它会继续等待直到 signalled* * 当它最终从这个方法返回它的中断状态就准备好了。* * 实现的注意事项* * 当调用此方法时,假定当前线程保持与 Condition 关联的 Lock。* * 由执行决定是否是这种情况,如果没有,应如何回应。 * * 通常,将抛出异常(例如IllegalMonitorStateException ),并且实现必须记录该事实。* * @throws InterruptedException - 如果当前线程中断(支持线程中断)*/void await() throws InterruptedException;/*** 使当前线程wait,直到 signal。 * * 与此 Condition 关联的 Lock 是原子方式释放,当前线程将禁用线程调度并且休眠直到下面三件事中的一件发生:** 一些其它线程调用本Condition的signal()方法,当前线程碰巧被选择作为被唤醒线程;** 一些其他的线程调用本Condition的signalAll();** 发生了“虚假唤醒 ”。* * 在所有情况下,在该方法返回之前,当前线程必须重新获取与该 Condition 关联的 Lock。* * 当线程返回时,保证持有该锁。 * * 如果当前线程的中断状态是在它进入这个方法时设置的,或者它在等待时被中断,它将继续等待直到发出信号。* * 当它最终从此方法返回时,它的中断状态仍将被设置。 * * 实现的注意事项 * * 调用此方法时,假定当前线程持有与此 Condition 关联的 Lock。* * 这取决于执行情况,以确定是否是这种情况,如果不是,如何应对。* * 通常,会抛出异常(例如IllegalMonitorStateException),实现必须记录该事实。*/void awaitUninterruptibly();/*** 使当前线程等待,直到发出信号或中断,或者经过指定的等待时间。* * 与此条件相关联的锁以原子方式释放,当前线程出于线程调度目的被禁用,并处于休眠状态,直到发生以下五种情况之一: * * 另一个线程为此条件调用signal方法,而当前线程恰好被选为要唤醒的线程;* * 其他线程为此条件调用signalAll方法;* * 其他线程中断当前线程,支持中断线程挂起;* * 经过指定的等待时间;* * 出现“虚假唤醒”。 * * 在所有情况下,在该方法返回之前,当前线程必须重新获取与该 Condition 关联的 Lock。* * 当线程返回时,保证持有该锁。 * * 如果当前线程: * * 在进入此方法时设置其中断状态;* * 或 等待时中断,支持线程挂起中断, 然后抛出InterruptedException并清除当前线程的中断状态。* * 在第一种情况下,没有规定是否在释放锁之前进行中断测试。 * * 该方法返回给定返回时提供的nanosTimeout值的剩余等待纳秒数的估计值,如果超时,则返回小于或等于零的值。* * 在等待返回但等待的条件仍不成立的情况下,此值可用于确定是否重新等待以及等待多长时间。* * 此方法的典型用途如下: * * boolean aMethod(long timeout, TimeUnit unit) {*   long nanos = unit.toNanos(timeout);*   lock.lock();*   try {*     while (!conditionBeingWaitedFor()) {*       if (nanos <= 0L)*         return false;*       nanos = theCondition.awaitNanos(nanos);*     }*     // ...*   } finally {*     lock.unlock();*   }* }}* * 设计说明:此方法需要纳秒参数,因此以避免在报告剩余时间时出现截断错误。* * 这样的精度损失将使程序员很难确保总等待时间不会系统性地缩短重新等待时指定的值。* * 实现注意事项:* * 假设当前线程持有与此关联的锁  Condition 调用此方法时。由实现者来确定这是否正确* * 如果没有,如何回应。通常,会出现异常抛出(例如{@link IllegalMonitorStateException})和执行工作必须记录这一事实。* * 一个实现比普通实现更倾向于响应中断方法返回对信号的响应,或指示经过的时间指定的等待时间。* * 无论哪种情况,实施必须确保信号被重定向到另一个等待线程,如果有一个。* * @param nanosTimeout 等待的最长时间,以纳秒为单位* @return nanosTimeout 值减去等待从此方法返回所花费的时间。正值可以用作对该方法的后续调用以完成等待所需时间。小于或等于零的值表示没有剩余时间。* @throws InterruptedException 如果当前线程被中断(支持挂线中断)(*/long awaitNanos(long nanosTimeout) throws InterruptedException;/*** 使当前线程等待,直到发出信号或中断,或者经过指定的等待时间。此方法在行为上等同于: 单位时间>0* * @param time 最大等待时间* @param unit 时间单位* @return 如果在从方法返回之前检测到等待时间已过为 false,否则则是 true* @throws InterruptedException 如果当前线程被中断(支持挂线中断)*/boolean await(long time, TimeUnit unit) throws InterruptedException;/*** 使当前线程等待,直到发出信号或被中断,或者指定的截止时间已过。 * * 与此条件相关联的锁以原子方式释放,当前线程出于线程调度目的被禁用,并处于休眠状态,直到发生以下五种情况之一: * * 另一个线程为此条件调用signal方法,而当前线程恰好被选为要唤醒的线程;* * 或 其他线程为此条件调用signalAll方法;* * 或 其他线程中断当前线程,支持中断线程挂起;* * 或 超过规定期限的;* * 或 出现“虚假唤醒”。 * * 在所有情况下,在该方法返回之前,当前线程必须重新获取与该条件相关联的锁。当线程返回时,保证持有该锁。 * * 如果当前线程: * * 在进入此方法时设置其中断状态;* * 或 等待时中断,支持线程挂起中断, 然后抛出InterruptedException并清除当前线程的中断状态。* * 在第一种情况下,没有规定是否在释放锁之前进行中断测试。 返回值表示截止日期是否已过,可按如下方式使用:* * boolean aMethod(Date deadline) {*   boolean stillWaiting = true;*   lock.lock();*   try {*     while (!conditionBeingWaitedFor()) {*       if (!stillWaiting)*         return false;*       stillWaiting = theCondition.awaitUntil(deadline);*     }*     // ...*   } finally {*     lock.unlock();*   }* }}* * 实现的注意事项* * 假设当前线程持有与此关联的锁{@code Condition}调用此方法时。由实现者来确定这是否正确* * 如果没有,如何回应。通常,会出现异常抛出(例如{@link IllegalMonitorStateException})和执行工作必须记录这一事实。* * 一个实现比普通实现更倾向于响应中断方法返回对信号的响应,或over表示通过在指定的截止日期。* * 无论哪种情况,实施必须确保如果需要,信号将重定向到另一个等待线程。* * @param deadline 等待的绝对时间* @return 如果返回时截止日期已过则为 false ,否则 true* @throws InterruptedException 如果当前线程被中断(支持挂线中断)*/boolean awaitUntil(Date deadline) throws InterruptedException;/*** 唤醒一个正在等待的线程。 * * 如果有任何线程正在等待此条件,则会选择一个线程进行唤醒。* * 然后该线程必须在从wait返回之前重新获取锁。 * * 实现的注意事项 * * 当调用此方法时,实现可能(并且通常确实)要求当前线程持有与此条件相关联的锁。* * 实现必须记录这个先决条件,以及在没有锁的情况下所采取的任何操作。* * 通常,会抛出异常,例如IllegalMonitorStateException*/void signal();/*** 唤醒所有等待的线程。 * * 如果有线程在此条件下等待,那么它们都会被唤醒。* * 每个线程必须重新获取锁,然后才能从wait返回。 * * 实现的注意事项 ** 当调用此方法时,实现可能(并且通常确实)要求当前线程持有与此条件相关联的锁。** 实现必须记录这个先决条件,以及在没有锁的情况下所采取的任何操作。** 通常,会抛出异常,例如IllegalMonitorStateException*/void signalAll();}

实践

package com.shockang.study.java.concurrent.lock;import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;public class ReenterLockCondition implements Runnable {public static ReentrantLock lock = new ReentrantLock();public static Condition condition = lock.newCondition();@Overridepublic void run() {try {lock.lock();condition.await();System.out.println("Thread is going on");} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public static void main(String[] args) throws InterruptedException {ReenterLockCondition tl = new ReenterLockCondition();Thread t1 = new Thread(tl);t1.start();Thread.sleep(2000);//通知线程t1继续执行lock.lock();condition.signal();lock.unlock();}
}

控制台输出

Thread is going on

说明

第 8 行代码通过 lock 生成一个与之绑定的 Condition 对象。

第 14 行代码要求线程在 Condition 对象上进行等待。

第 30 行代码由主线程 main 发出通知,告知等待在 Condition 上的线程可以继续执行了

与 Object.wait() 方法和 notify() 方法一样,当线程使用 Condition.await() 方法时,要求线程持有相关的重入锁,在 Condition.await() 方法调用后,这个线程会释放这把锁

同理,在 Condition.signal() 方法调用时,也要求线程先获得相关的锁。

在 signal() 方法调用后,系统会从当前 Condition 对象的等待队列中唤醒一个线程。

一旦线程被唤醒,它会重新尝试获得与之绑定的重入锁,一旦成功获取,就可以继续执行了。

因此,在 signal() 方法调用之后,一般需要释放相关的锁,让给被唤醒的线程,让它可以继续执行。

比如,在本例中,第 31 行代码就释放了重入锁,如果省略第 31 行,那么,虽然已经唤醒了线程 t1 ,但是由于它无法重新获得锁,因而也就无法真正的继续执行。

Condition 是什么?怎么用?相关推荐

  1. 【java线程】锁机制:synchronized、Lock、Condition

    [Java线程]锁机制:synchronized.Lock.Condition 原创 2013年08月14日 17:15:55 标签:Java /多线程 74967 http://www.infoq. ...

  2. java race condition_java多线程(一)Race Condition现象及产生的原因

    什么是Race Condition 首先,什么是Race Condition呢,Race Condition中文翻译是竞争条件,是指多个进程或者线程并发访问和操作同一数据且执行结果与访问发生的特定顺序 ...

  3. Python标准库threading模块Condition原理浅析

    Python标准库threading模块Condition原理浅析 本文环境python3.5.2 threading模块Condition的实现思路 在Python的多线程实现过程中,在Linux平 ...

  4. icp mysql_MySQL · 特性分析 · Index Condition Pushdown (ICP)

    前言 上一篇文章 提过,我们在之后的文章中会从 optimizer 的选项出发,系统的介绍 optimizer 的各个变量,包括变量的原理.作用以及源码实现等,然后再进一步的介绍优化器的工作过程(SQ ...

  5. pandas使用apply函数基于条件(if condition)生成新的数据列

    pandas使用apply函数基于条件(if condition)生成新的数据列 目录 pandas使用apply函数基于条件(if condition)生成新的数据列

  6. ReentrantLock中的Condition(等待和唤醒)

    Condition 类的 awiat 方法和 Object 类的 wait 方法等效 Condition 类的 signal 方法和 Object 类的 notify 方法等效 Condition 类 ...

  7. bean ref的bean可以做判断吗_Spring Boot @Condition 注解,组合条件你知道吗

    写在前面 当我们构建一个 Spring 应用的时候,有时我们想在满足指定条件的时候才将某个 bean 加载到应用上下文中, 在Spring 4.0 时代,我们可以通过 @Conditional 注解来 ...

  8. SAP ME12 修改采购信息记录,系统提示:Condition type P000 does not allow supplementary conditions

    SAP ME12 修改采购信息记录,系统提示:Condition type P000 does not allow supplementary conditions 1,执行事务代码ME12,进入采购 ...

  9. SAP SD 基础知识之定价中的条件技术(Condition Technique in Pricing)

    SAP SD 基础知识之定价中的条件技术(Condition Technique in Pricing) 一,定价程序Pricing Procedure 所有定价中允许的条件类型都包含在定价程序中: ...

  10. ReentrantLock和Condition理解及应用

    Condition: Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备( signal 或者 signalAll方法被带 ...

最新文章

  1. 2021-2027年中国一次成型光学玻璃行业市场分析及投资潜力研究报告
  2. HDU2648(map的应用)
  3. 禁用software reporter tool.exe 解决CPU高占用率的问题
  4. SaltStack部署
  5. 千万级大表如何更快速的创建索引_分享一份生产环境mysql数据库大表归档方案,值得收藏...
  6. 内存条能4+8混插吗?_笔记本内存条双通道提升有多大?实测FORESEE,你知道好处在哪吗...
  7. mysql自增序列nextval并发_[DB][MySql]关于取得自增字段的值、及@@IDENTITY 与并发性问题...
  8. [渝粤教育] 西南科技大学 程序设计语言VB 在线考试复习资料(2)
  9. 这10道大厂Java面试题,我敢打赌90%的人都不会!!!
  10. LuoguP3045牛券Cow Coupons
  11. 马尔科夫蒙特卡罗方法
  12. C语言中文网 读后感
  13. 国内Linux笔记天花板,不接受反驳!
  14. uni-app 基础之常用组件(2)基础内容
  15. vue合并表格excel导出_Vue实现导出excel表格功能
  16. excel数字排序1后面是2而不是10
  17. vue 生命周期-activated
  18. 精心整理了100+Python字符串常用操作,备用
  19. R155附录5 Part A
  20. vivo计算机隐藏游戏,vivoX30功能使用教程 隐藏游戏图标不让孩子发现

热门文章

  1. 聊天机器人微信表情个性化 回复 部分示例
  2. python学习实践--爬取猫眼电影排行
  3. 微信小程序——时间安卓、苹果获取时间戳表现不一致
  4. 【推荐系统】向量召回算法 HNSW
  5. java构造函数调用其他程序的顺序,java关于继承中构造函数的调用顺序
  6. c语言装逼编程,教你用C语言写一个好玩的万年历,必备的装逼神器
  7. 如果c盘空间太小,vs安装不下怎么办?
  8. Python学习Day1
  9. 如何为客户提供良好的客户体验?
  10. android 监控来电(草稿)