系统性学习,移步IT-BLOG

Java 对象拥有一组监视方法:wait()、wait(long timeout)、notify() 以及 notifyAll() 方法,这些方法与 synchronized 同步关键字配合,可以实现等待/通知模式,进行线程之间的通讯。Condition 接口也提供了类似的方法,与 Lock 配合可以实现等待/通知模式,但两者使用方法和功能上存在差异。我们对比学习一下:

对比项 Object 对象 Condition 接口
前置条件 获取对象的锁 调用Lock.lock()获取锁,通过Lock.newCondition()获取Condition对象
调用方法 object.wait() condition.await()
等待队列(wait queue)个数 一个 多个
当前线程释放锁并进入等待状态 支持 支持
阻塞时,响应中断interrupt() 响应中断 响应中断 OR 不响应中断
当前线程释放锁,进入超时状态X(long timeout) 支持 支持
当前线程释放锁并进入等待状态到将来的某个时间 不支持 支持
唤醒等待队列中的一个线程 支持 支持
唤醒等待队列中的全部线程 支持 支持

一、Condition 接口


当线程调用 Condition 中的方法时,需要提前获取到 Condition 关联的锁(Condition 对象是由 Lock 对象的 newCondition() 方法创建),也就是 Condition 依赖 Lock 对象。

 1 public class ConditionUseCase {
2 Lock lock = new ReentrantLock();
3 Condition condition = lock.newCondition();
4 public void conditionWait() throws InterruptedException{
5 lock.lock();
6 try {
7 condition.await();
8 } finally {
9 lock.unlock();
10 }
11 }
12
13 public void conditionSignal() throws InterruptedException{
14 lock.lock();
15 try {
16 condition.signal();
17 } finally {
18 lock.unlock();
19 }
20 }
21 }

一般都会将 Condition 对象作为成员。当调用 await() 方法后,当前线程会释放锁并在此等待,而其他线程调用 Condition 对象的signal() 方法,通知当前线程后,当前线程才从 await() 方法返回,并且在返回前已经获取了锁。Condition 定义方法:

方法名称 描述
void await() throws InterruptedException 当前线程进入等待状态直到被通知(signal)或中断,当前线程将进入运行状态且从 await() 方法返回的情况,包括:其他线程调用该 Condition 的 signal()或signalAll()方法,或者被其他线程调用 interrupt 中断。如果当前线程从 await()方法返回,表明该线程已经获取了 Condition 对象所对应的锁。
void awaitUninterruptibly() 当前线程进入等待状态直到被通知,对中断不敏感。
long awaitNanos(long nanosTimeout) throws InterruptedException 当前线程进入等待状态直到被通知、中断或者超时。返回值表示剩余时间,如果在 nanosTimeout 纳秒之前被唤醒,那么返回值就是(nanosTimeout-实际消耗)返回值如果是0或者负数表示超时。
boolean awaitUntil(Date deadline) throws InterruptedException 当前线程进入等待状态直到被通知、中断或者到某个时间。如果没有到指定时间就被通知,方法返回true,否则,表示到了指定时间,方法返回 false。
void signal() 唤醒一个等待在 Condition 上的线程,该线程从等待方法返回前必须获得与Condition 相关的锁。
void signalAll() 唤醒所有等待在 Condition 上的线程,能够从等待方法返回的线程必须获得与Condition相关的锁。

二、Condition 的实现分析


ConditionObject 是同步器 AQS(AbstractQueuedSynchronize)的内部类,因为 Condition 的操作需要获取相关联的锁,所以作为同步器的内部类也比较合理。每个 Condition 对象都包含着一个队列(等待队列),该队列是 Condition 对象实现等待/通知功能的关键。
【1】等待队列:等待队列是一个 FIFO 的队列,队列中包含的是在 Condition 对象上等待的线程。如果一个线程调用 Condition.await() 方法,那么该线程就会释放锁、构造成节点加入到等待队列中。事实上,节点的定义复用了同步器中节点的定义,同步队列和等待队列中节点类型都是同步器的静态内部类 AbstractQueuedSynchronizer.Node。一个 Condition 包含一个等待队列,Condition 拥有首节点(firstWaiter)和尾结点(lastWaiter)。当前线程调用 Condition.await() 方法,将会以当前线程构造节点,并将节点从尾部加入等待队列。
【2】等待:调用 Condition 的 await() 方法(或者以 await 开头的方法),会使当前线程进入等待队列并释放锁,同时线程进入等待状态。如果不是通过调用 Condition.signal()方法唤醒,而是通过其他线程调用interrupt() 中断,则会抛出interruptedException 异常。

 1 public final void await() throws InterruptedException {
2 if (Thread.interrupted())
3 throw new InterruptedException();
4 //当前线程构造成节点加入等待队列
5 Node node = addConditionWaiter();
6 //释放同步状态,也就是释放锁
7 int savedState = fullyRelease(node);
8 int interruptMode = 0;
9 while (!isOnSyncQueue(node)) {
10 LockSupport.park(this);
11 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
12 break;
13 }
14 if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
15 interruptMode = REINTERRUPT;
16 if (node.nextWaiter != null) // 取消时清除
17 unlinkCancelledWaiters();
18 if (interruptMode != 0)
19 reportInterruptAfterWait(interruptMode);
20 }

【3】通知:调用 Condition 的 signal() 方法,将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒之前,会将节点移到同步队列中。调用该方法的前提就是必须获取锁。signal 源码如下:通过调用同步器的 enq(Node node)方法,等待队列中的头节点线程安全移动到同步队列。当节点移动到同步队列后,当前线程再使用 LockSupport 的unpark唤醒该节点。

1 public final void signal() {
2 //isHeldExclusively 检查当前线程是否获取了锁
3 if (!isHeldExclusively())
4 throw new IllegalMonitorStateException();
5 //获取第一个Node
6 Node first = firstWaiter;
7 if (first != null)
8 doSignal(first);
9 }

被唤醒的线程,将从 await() 方法的 while 循环中退出(isOnSyncQueue(Node node))是否处于同步队列,方法返回 true 表示在同步队列,然后调用同步器的 acquireQueued() 方法加入到获取同步状态竞争中。被唤醒的线程将从调用的 await() 方法返回,此时该线程已经成功获取了锁。Condition的 signalAll() 方法,相当于对等待中的每一个节点执行了一次 signal() 方法,效果就是将等待队列中所有节点转移到同步队列 AQS 中,并唤醒每个节点的线程。

Condition 接口的更多相关文章

  1. 6.类似Object监视器方法的Condition接口

    在<1.有关线程.并发的基本概念>中,我们利用synchronized关键字.Queue队列.以及Object监视器方法实现了生产者消费者,介绍了有关线程的一些基本概念.Object类提供 ...

  2. Java的LockSupport工具,Condition接口和ConditionObject

    在之前我们文章(关于多线程编程基础和同步器),我们就接触到了LockSupport工具和Condition接口,之前使用LockSupport工具来唤醒阻塞的线程,使用Condition接口来实现线程 ...

  3. 并发之lock的condition接口

    13.死磕Java并发-----J.U.C之Condition 12.Condition使用总结 11.Java并发编程系列之十七:Condition接口 === 13.死磕Java并发-----J. ...

  4. java中的锁之Lock接口与Condition接口

    一.Lock源码. 1.是一个接口.一共有6个方法. 2.方法详细如下: (1)当前线程尝试获取锁.结果分两种情况,一是成功获取到锁,则返回:二是获取锁失败,则一直等待.不响应中断请求. (2)当前线 ...

  5. Condition接口

    <Java并发编程艺术>读书笔记 Condition介绍 任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object中),主要包括wait().wait(long ...

  6. Java并发Condition接口

    java.util.concurrent.locks.Condition接口提供一个线程挂起执行的能力,直到给定的条件为真. Condition对象必须绑定到Lock,并使用newCondition( ...

  7. Condition接口及其主要实现类ConditionObject源码浅析

    1.引子 任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait().wait(long timeout).notify()以及notifyAll() ...

  8. Lock接口之Condition接口

    之前在写显示锁的是后,在显示锁的接口中,提到了new Condition这个方法,这个方法会返回一个Condition对象 简单介绍一下 Condition接口: 任意一个Java对象,都拥有一组监视 ...

  9. Java 并发编程之 Condition 接口

    本文部分摘自<Java 并发编程的艺术> 概述 任意一个 Java 对象,都拥有一个监视器方法,主要包括 wait().wait(long timeout).notify() 以及 not ...

  10. 精尽Spring Boot源码分析 - Condition 接口的扩展

    该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...

随机推荐

  1. 我的第三次JAVA作业

    ------------恢复内容开始------------ 1.对象与对象引用的区别是什么? 请举例说明 创建对象被分配在堆中,对象引用分配在栈中. eg. new FighterPlane(); ...

  2. Linux 使用Bind提供域名解析服务

    DNS域名解析服务 相较于由数字构成的IP地址,域名更容易被理解和记忆,所以我们通常更习惯通过域名的方式来访问网络中的资源.但是,网络中的计算机之间只能基于IP地址来相互识别对方的身份,而且要想在互联 ...

  3. element UI 选择时间点(可选择某一个或者多个时间点)

    html代码如下: <el-date-picker ref="datesRef" type="dates" v-model="searchObj ...

  4. c++11 智能指针学习汇总

    c++为什么要引入智能指针? C/C++ 语言最为人所诟病的特性之一就是存在内存泄露问题,因此后来的大多数语言都提供了内置内存分配与释放功能,有的甚至干脆对语言的使用者屏蔽了内存指针这一概念.这里不置 ...

  5. 创建ZBLOG数据库

    1.下载ZBLOG待测系统,并解压缩 打开内容如下 2.打开小皮面板的网站,点击网站->管理->打开根目录,然后把里面所有内容删除,粘贴下载好打开的内容 3.然后重启Nginx 4.浏览器 ...

  6. 处理时间转换不正确-Springboot、springclound、feign、http请求

    SpringBoot.SpringCloud.feign.前后端时间解析不正确时,我们可以自定义HttpMessageConverters,以达到我们希望的结果 参考链接:https://www.cn ...

  7. 【逆向】CVE-2017-8570漏洞分析调试技巧

    前言 CVE-2017-8570是一个逻辑型漏洞,该漏洞利用复合Moniker绕过了CVE-2017-0199的更新补丁,可以在Office文档中执行任意SCT(Windows Script Comp ...

  8. Python turtle print TaiChi

    import turtle turtle.pensize(20) turtle.pencolor("black") turtle.penup() turtle.goto(0,300 ...

  9. Android studio应用

    菜单的使用 public class FirstActivity extends AppCompatActivity { @Override protected void onCreate(Bundl ...

  10. C# POST GET请求方式汇总

    /// <summary> /// POST方式提交 application/json /// </summary> /// <param name="post ...