Java并发:Condition接口
Condition 接口与 Lock 配合实现了等待 / 通知模式,这个和 Object 的监视器方法(wait、notify、notifyAll 等方法)一样,都是实现了等待 / 通知模式,但这两者在使用的方式以及功能特性上还是有差别的。
| 对比项 | Object Monitor Methods | Condition |
| 前置条件 | 获取对象的锁 |
调用Lock.lock()获取锁 调用Lock.newCondition()获取Condition对象 |
| 调用方式 | 直接调用,如:Object.wait() | 直接调用,如:condition.await() |
| 等待队列个数 | 一个 | 多个 |
| 当前线程释放锁并进入等待状态 | 支持 | 支持 |
|
当前线程释放锁并进入等待状态, 在等待状态中不响应中断 |
不支持 | 支持 |
| 当前线程释放锁并进入等待超时状态 | 支持 | 支持 |
| 当前现场释放锁并进入等待状态到将来某个时间 | 不支持 | 支持 |
| 唤醒等待队列中的一个线程 | 支持 | 支持 |
| 唤醒等待队列中的所有线程 | 支持 | 支持 |
在 Object 监视模型中,一个对象有一个同步队列和一个等待队列,而 AQS 拥有一个同步队列和多个等待队列。
Object 的监视模型:

Condition 的同步队列与等待队列:

Condition 部分方法以及描述:
| 方法名称 | 描述 |
void await() throws |
当前线程进入等待状态,直到被通知(signal)或中断, 当前线程才会进入运行状态且从此await方法返回(表明 该线程已经获取了Condition对象对应的锁),包括: 其他线程(调用了interrupt()方法中断了当前线程)调用 该 Condition 的 signal() 或 signalAll()方法,而当前线 程被选中唤醒 |
void awaitUninterruptibly() |
当前线程进入等待状态,知道被通知,对中断不敏感 |
long awaitNanos(long nanosTimeout) |
当前线程进入等待状态直到被通知、中断或者是超时, 返回值代表剩余的时间,如果在nanosTimeout纳秒 之前被唤醒,返回值是(nanosTimeout - 实际耗时)。 若返回的是0或者负数,超时了。 |
boolean awaitUntil(Date deadline) |
当前线程进入等待状态直到被通知、中断或者是到某个 时间。没有到指定时间就被通知,返回true;否则, 返回false |
void signal() |
唤醒一个等待在Condition上的线程,该线程从等待方法 返回前必须获得与Condition相关的锁 |
void signalAll() |
唤醒所有等待在Condition上的线程,能够从等待方法 返回的线程必须获得与Condition相关联的锁 |
Condition 的实现
① 等待队列
前面的 Condition 的同步队列与等待队列,队列中的每个节点都包含一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用Condition.await()方法,那么该线程将会释放锁、构造成节点加入等待队列进入等待状态。同步队列和等待队列的节点类型都是AQS中的静态内部类Node。Condition是AQS中的静态内部类。
② 等待
调用Condition的await方法,会使当前线程进入等待队列并释放锁,同时线程状态变成等待状态。当从await方法返回时,当前线程一定获取了Condition相关联的锁。
1 public final void await() throws InterruptedException {
2 if (Thread.interrupted())
3 throw new InterruptedException();
4 Node node = addConditionWaiter(); // 当前线程加入等待队列
5 int savedState = fullyRelease(node); // 释放同步状态,也就是释放锁
6 int interruptMode = 0;
7 while (!isOnSyncQueue(node)) {
8 LockSupport.park(this);
9 if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
10 break;
11 }
12 if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
13 interruptMode = REINTERRUPT;
14 if (node.nextWaiter != null) // 线程被取消的话要清除节点
15 unlinkCancelledWaiters();
16 if (interruptMode != 0)
17 reportInterruptAfterWait(interruptMode);
18 }
如果队列的角度来看await()方法,当调用await()方法时,相当于把同步队列的首节点(获取了锁的节点)移动到Condition的等待队列中,实际上是通过addConditionWaiter()方法把当前线程构造成一个新的节点并将其加入等待队列中。

③ 通知
调用该方法的前置条件是要获取了锁,方法首先用isHeldExclusively()检查,接着获取等待队列的首节点,将其移动到同步队列并使用LockSupport工具类唤醒节点中的线程。
1 public final void signal() {
2 if (!isHeldExclusively())
3 throw new IllegalMonitorStateException();
4 Node first = firstWaiter;
5 if (first != null)
6 doSignal(first);
7 }
1 private void doSignal(Node first) {
2 do {
3 if ( (firstWaiter = first.nextWaiter) == null)
4 lastWaiter = null;
5 first.nextWaiter = null;
6 } while (!transferForSignal(first) &&
7 (first = firstWaiter) != null);
8 }
1 final boolean transferForSignal(Node node) {
2 /*
3 * 如果无法更改waitStatus,则该节点会被取消
4 */
5 if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
6 return false;
7
8 /*
9 * 拼接到队列上并尝试设置前任的waitStatus以指示线程(可能)正在等待。
10 * 如果取消或尝试设置waitStatus失败,请唤醒以重新同步
11 * (在这种情况下,waitStatus可能是暂时性且无害的错误)
12 */
13 Node p = enq(node);
14 int ws = p.waitStatus;
15 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
16 LockSupport.unpark(node.thread);
17 return true;
18 }
节点从等待队列移动到同步队列:

Java并发:Condition接口的更多相关文章
- Java并发Condition接口
java.util.concurrent.locks.Condition接口提供一个线程挂起执行的能力,直到给定的条件为真. Condition对象必须绑定到Lock,并使用newCondition( ...
- JAVA并发-Condition
简介 在没有Lock之前,我们使用synchronized来控制同步,配合Object的wait().notify()系列方法可以实现等待/通知模式.在Java SE5后,Java提供了Lock接口, ...
- 在Java的Condition接口【唤醒全部线程】
在Java的Condition接口中,存在的几个方法跟Synchronized中的wait(),waitall(),wait(time ^),这个几个方法一一对应起来,但是在Lock.newCondi ...
- Java并发ReadWriteLock接口
java.util.concurrent.locks.ReadWriteLock接口允许一次读取多个线程,但一次只能写入一个线程. 读锁 - 如果没有线程锁定ReadWriteLock进行写入,则多线 ...
- Java并发Lock接口
java.util.concurrent.locks.Lock接口用作线程同步机制,类似于同步块.新的锁定机制更灵活,提供比同步块更多的选项. 锁和同步块之间的主要区别如下: 序列的保证 - 同步块不 ...
- Java并发AtomicLong接口
java.util.concurrent.atomic.AtomicLong类提供了可以被原子地读取和写入的底层long值的操作,并且还包含高级原子操作. AtomicLong支持基础long类型变量 ...
- Java 并发编程之 Condition 接口
本文部分摘自<Java 并发编程的艺术> 概述 任意一个 Java 对象,都拥有一个监视器方法,主要包括 wait().wait(long timeout).notify() 以及 not ...
- Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...
- Java的LockSupport工具,Condition接口和ConditionObject
在之前我们文章(关于多线程编程基础和同步器),我们就接触到了LockSupport工具和Condition接口,之前使用LockSupport工具来唤醒阻塞的线程,使用Condition接口来实现线程 ...
随机推荐
- Spring5(七)——AOP注解
一.AOP注解 1.介绍 上一节介绍了 AspectJ 框架如何实现 AOP,具体的实现方式是通过 xml 来进行配置的.xml 方式思路清晰,便于理解,但是书写过于麻烦.这一节介绍注解的方式来进行 ...
- jdbc核心技术-宋红康
视频地址 JDBC核心技术 第1章:JDBC概述 1.1 数据的持久化 持久化(persistence):把数据保存到可掉电式存储设备中以供之后使用.大多数情况下,特别是企业级应用,数据持久化意味着将 ...
- 简单操作:10分钟实现在kubernetes(k8s)里面部署服务器集群并访问项目(docker三)
前言 经过docker安装.k8s开启并登录,我们终于到 "部署k8s服务器集群并访问项目" 这一步了,实现的过程中有太多坑,好在都填平了,普天同庆. 在进行当前课题之前,我们需要 ...
- python中字典按键、值进行排序
看到排序,就不禁想起python中的sort和sorted sort是列表中的方法,用于对列表进行排序(改变的是原列表,不返回新列表) 用法: list.sort(key=None,reverse=T ...
- Catch That Cow----BFS
Catch That Cow Description 农夫知道一头牛的位置,想要抓住它.农夫和牛都位于数轴上 ,农夫起始位于点 N(0<=N<=100000) ,牛位于点 K(0<= ...
- 【C++基础教程】第五课
上次的作业答案,非常简单. 第一题: 我们需要知道,字符(char类型)在计算机中存储的时候,是把这个字符对应的代码(专业术语叫做编码)进行存储.例如,换行符'\n'的代码就是10,'0'对应的代码就 ...
- ios web 媒体查询兼容
原文:https://blog.csdn.net/dear_zx/article/details/82785250 防止链接丢失,复制一下 兼容iphone4/4s: @media (device-h ...
- frida的安装教程-配合夜神模拟器
Frida安装 一.PC端安装 1. 安装frida 默认安装最新版的Frida pip install frida 因为我用的是夜神模拟器,可能不支持最新版,所以下载的之前版本. pip insta ...
- 鸿蒙内核源码分析(进程镜像篇)|ELF是如何被加载运行的? | 百篇博客分析OpenHarmony源码 | v56.01
百篇博客系列篇.本篇为: v56.xx 鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的? | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ...
- windows terminal+wsl+neovim配置过程杂记
长期记录,草稿 coc依赖于node,直接sudo apt intsll node得到的版本是10.x,无法满足要求, 这篇博客介绍了安装新版node的方法https://www.cnblogs.co ...