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接口来实现线程 ...
随机推荐
- (6)java Spring Cloud+Spring boot+mybatis企业快速开发架构之SpringCloud-Spring Boot项目详细搭建步骤
在 Spring Tools 4 for Eclipse 中依次选择 File->New->Maven Project,然后在出现的界面中按图所示增加相关信息. <paren ...
- 一些PHP选项参数相关的函数
关于 PHP 的配置,我们大多数情况下都是去查看 php.ini 文件或者通过命令行来查询某些信息,其实,PHP 的一些内置函数也可以帮助我们去查看或操作这些配置参数.比如之前我们学习过的 关于php ...
- vue报错 Uncaught TypeError: Cannot read property of null
有可能是点击a标签,但是a标签有click事件,未阻止默认事件导致报错,开始都看不出来是什么错误
- Jmeter系列(22)- 常用逻辑控制器(1) | 随机控制器Random Controller
随机控制器(Random Controller) 该控制器下的请求,请求顺序随机,适用场景一般为顺序性依赖不强的请求,比如:下载文件:浏览商品:访问查询接口 随机控制器下的请求随机,如果勾选了[忽略控 ...
- redis 与java的连接 和集群环境下Session管理
redis 的安装与设置开机自启(https://www.cnblogs.com/zhulina-917/p/11746993.html) 第一步: a) 搭建环境 引入 jedis jar包 co ...
- python 文件夹扫描
扫描指定文件夹下的文件.或者匹配指定后缀和前缀的函数. 假设要扫描指定文件夹下的文件,包含子文件夹,调用scan_files("/export/home/test/") 假设要扫描 ...
- 鸿蒙内核源码分析(用栈方式篇) | 程序运行场地谁提供的 | 百篇博客分析OpenHarmony源码 | v20.04
百篇博客系列篇.本篇为: v20.xx 鸿蒙内核源码分析(用栈方式篇) | 程序运行场地谁提供的 | 51.c.h .o 精读内核源码就绕不过汇编语言,鸿蒙内核有6个汇编文件,读不懂它们就真的很难理解 ...
- POJ3734-Blocks【EGF】
正题 题目链接:http://poj.org/problem?id=3734 题目大意 用思种颜色给\(n\)个格子染色,要求前两种颜色出现偶数次,求方案. \(1\leq T\leq 100,1\l ...
- ThreadLocal底层
1. 首先我们来看一下他的使用 public class ThreadLocalTest { public static void main(String[] args) { MyThread thr ...
- 从Gartner与IDC三大行业报告,看国产RPA的市场规模与未来发展
从Gartner与IDC三大行业报告,看国产RPA的市场规模与未来发展 Gartner与IDC的三份报告,描绘出中国RPA的市场规模与未来宏图 文/王吉伟 近期的国产RPA,可谓捷报频频.三个重量级行 ...