Condition 接口
系统性学习,移步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 接口的更多相关文章
- 6.类似Object监视器方法的Condition接口
在<1.有关线程.并发的基本概念>中,我们利用synchronized关键字.Queue队列.以及Object监视器方法实现了生产者消费者,介绍了有关线程的一些基本概念.Object类提供 ...
- Java的LockSupport工具,Condition接口和ConditionObject
在之前我们文章(关于多线程编程基础和同步器),我们就接触到了LockSupport工具和Condition接口,之前使用LockSupport工具来唤醒阻塞的线程,使用Condition接口来实现线程 ...
- 并发之lock的condition接口
13.死磕Java并发-----J.U.C之Condition 12.Condition使用总结 11.Java并发编程系列之十七:Condition接口 === 13.死磕Java并发-----J. ...
- java中的锁之Lock接口与Condition接口
一.Lock源码. 1.是一个接口.一共有6个方法. 2.方法详细如下: (1)当前线程尝试获取锁.结果分两种情况,一是成功获取到锁,则返回:二是获取锁失败,则一直等待.不响应中断请求. (2)当前线 ...
- Condition接口
<Java并发编程艺术>读书笔记 Condition介绍 任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object中),主要包括wait().wait(long ...
- Java并发Condition接口
java.util.concurrent.locks.Condition接口提供一个线程挂起执行的能力,直到给定的条件为真. Condition对象必须绑定到Lock,并使用newCondition( ...
- Condition接口及其主要实现类ConditionObject源码浅析
1.引子 任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object上),主要包括wait().wait(long timeout).notify()以及notifyAll() ...
- Lock接口之Condition接口
之前在写显示锁的是后,在显示锁的接口中,提到了new Condition这个方法,这个方法会返回一个Condition对象 简单介绍一下 Condition接口: 任意一个Java对象,都拥有一组监视 ...
- Java 并发编程之 Condition 接口
本文部分摘自<Java 并发编程的艺术> 概述 任意一个 Java 对象,都拥有一个监视器方法,主要包括 wait().wait(long timeout).notify() 以及 not ...
- 精尽Spring Boot源码分析 - Condition 接口的扩展
该系列文章是笔者在学习 Spring Boot 过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring Boot 源码分析 GitHub 地址 进行阅读 Sprin ...
随机推荐
- 我的第三次JAVA作业
------------恢复内容开始------------ 1.对象与对象引用的区别是什么? 请举例说明 创建对象被分配在堆中,对象引用分配在栈中. eg. new FighterPlane(); ...
- Linux 使用Bind提供域名解析服务
DNS域名解析服务 相较于由数字构成的IP地址,域名更容易被理解和记忆,所以我们通常更习惯通过域名的方式来访问网络中的资源.但是,网络中的计算机之间只能基于IP地址来相互识别对方的身份,而且要想在互联 ...
- element UI 选择时间点(可选择某一个或者多个时间点)
html代码如下: <el-date-picker ref="datesRef" type="dates" v-model="searchObj ...
- c++11 智能指针学习汇总
c++为什么要引入智能指针? C/C++ 语言最为人所诟病的特性之一就是存在内存泄露问题,因此后来的大多数语言都提供了内置内存分配与释放功能,有的甚至干脆对语言的使用者屏蔽了内存指针这一概念.这里不置 ...
- 创建ZBLOG数据库
1.下载ZBLOG待测系统,并解压缩 打开内容如下 2.打开小皮面板的网站,点击网站->管理->打开根目录,然后把里面所有内容删除,粘贴下载好打开的内容 3.然后重启Nginx 4.浏览器 ...
- 处理时间转换不正确-Springboot、springclound、feign、http请求
SpringBoot.SpringCloud.feign.前后端时间解析不正确时,我们可以自定义HttpMessageConverters,以达到我们希望的结果 参考链接:https://www.cn ...
- 【逆向】CVE-2017-8570漏洞分析调试技巧
前言 CVE-2017-8570是一个逻辑型漏洞,该漏洞利用复合Moniker绕过了CVE-2017-0199的更新补丁,可以在Office文档中执行任意SCT(Windows Script Comp ...
- Python turtle print TaiChi
import turtle turtle.pensize(20) turtle.pencolor("black") turtle.penup() turtle.goto(0,300 ...
- Android studio应用
菜单的使用 public class FirstActivity extends AppCompatActivity { @Override protected void onCreate(Bundl ...
- C# POST GET请求方式汇总
/// <summary> /// POST方式提交 application/json /// </summary> /// <param name="post ...