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接口来实现线程 ...
随机推荐
- <input type="file">如何实现自定义样式
利用样式覆盖来实现效果:先看下原本和改变后的样式 1 <!doctype html> 2 <html> 3 <head> 4 <title>file自定 ...
- Tars | 第8篇 TarsJava Subset最终代码的执行流程与原理分析
目录 前言 1. SubsetConf配置项的结构 1.1 SubsetConf 1.2 RatioConfig 1.3 KeyConfig 1.4 KeyRoute 1.5 SubsetConf的结 ...
- K8s 开始
Kubernetes 是用于自动部署,扩展和管理容器化应用程序的开源系统.本文将介绍如何快速开始 K8s 的使用. 了解 K8s Kubernetes / Overview 搭建 K8s 本地开发测试 ...
- 我爬取交通学博士付费的GIS资源,每年被动收入2w很简单?
目录 1.背景介绍 2.技术路线 3.数据结果 4.数据分析 5.总结 6.后记 1.背景介绍 某周末闲来无事,顺手打开了CSDN,看到了一个人发布的收费GIS资源,售价是¥19.9,POI数据也有人 ...
- Orchar Core 创建一个模块化的ASP.NET Core应用程序
您将构建什么?您将构建一个模块化的ASP.NET Core MVC Web应用程序,类似于Orchard Core附带的示例"Hello World"应用程序.它包括一个Web应用 ...
- python处理权限关系一些注意事项
在模型类中多对多的关系(必须是对象) 如果要获取多对多表中的值,必须使用对象进行搜索,不能直接输入名称 例:这样才能查找到(下面例子是针对,组设置多对多关到用户,现在要根据用户获取相应的组) user ...
- 『GoLang』语法基础
标识符 字母或下划线开头 之后只能出现数字.字母.下划线 大小写敏感 Go语言关键字 break default func interface select case defer go map str ...
- Tidb使用
一.为什么使用Tidb 最近发现tidb在互联网圈大火,新生代的一个NewSql数据库 具体链接可以访问pincap的官网 https://www.pingcap.com/docs-cn/v3.0/ ...
- oracle 查看表结构语句
desc + 表名 describe命令 列出指定表的列定义,视图或同义词,或指定函数或存储过程的详述. 语法:desc[ribe] {[模式.]对象[@链接串]} 模式 表示对象驻留的架构.如果 ...
- yolov5实战之二维码检测
目录 1.前沿 2.二维码数据 3.训练配置 3.1数据集设置 3.2训练参数的配置 3.3网络结构设置 3.4训练 3.5结果示例 附录:数据集下载 1.前沿 之前总结过yolov5来做皮卡丘的检测 ...