系统性学习,移步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. 什么是跨域及如何解决、json和jsonp

    1.跨域: 出于浏览器的同源策略限制,同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互. 同源:即指在同一个域中,就是两个页面具有相同的协议(protocol),主机(host ...

  2. Cxf框架中@WebService注解的使用

    最近工作中总是不可避免的使用WebService来对接功能,经过自己一番摸索,总结出了一些使用方法,做一下记录: 记录了两个SpringBoot版本使用WebService的一些问题和用法,Sprin ...

  3. nmap扫描

    Nmap扫描 学习老师发的链接中的指令:https://www.cnblogs.com/nmap/p/6232207.html 下载了nmap软件 根据教学中的操作查找虚拟机的IP地址 并用主机对其扫 ...

  4. BZOJ1008 [HNOI2008]越狱 (快速幂,组合)

    题目大意 求\(m\)种数字组成的长度为\(n\)的序列的种数,序列中至少有一段连续的数字 分析 用可重排列的种数减去,相邻数字互不相同的序列种数 考虑相邻互不相同,第一个元素有\(m\)种可能,后面 ...

  5. MySQL事务MVCC、undolog和redolog

    MySql的MVCC多版本控制 undolog:回滚日志(保证一致性)只有在ReadCommited和RepeatableRead隔离级别有用 redolog:重写日志(保证持久性) 示例讲解 Rea ...

  6. Topsis法的python实现

    TOPSIS (Technique for Order Preference by Similarity to an Ideal Solution )法是C.L.Hwang和K.Yoon于1981年首 ...

  7. curl 查看响应时间

    curl -o /dev/null -s -w "time_namelookup:%{time_namelookup}\ntime_connect: %{time_connect}\ntim ...

  8. Navicat连接Mysql报错:Client does not support authentication protocol requested by server(转载)

    Navicat连接MySQL Server8.0版本时出现Client does not support authentication protocol requested  by server:解决 ...

  9. pandas(随时更新)

    pandas处理一个表中的一列数据被另一个表中的另一列数据替换: df1=pd.DataFrame({'id':[1,2,3],'name':['Andy1','Jacky1','Bruce1']}) ...

  10. OSIDP-文件管理-12(end)

    概述 文件特性:可长期存储:可在进程间共享:有特定结构. 文件系统提供对文件操作的功能接口:创建.删除.打开.关闭.读和写. 域(field):基本数据单元,一个域包含一个值. 记录(record): ...