系统性学习,移步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. 5G智慧灯杆系统在智慧街区的应用

    智慧化的路灯作为一个高度集成的项目,是智慧城市在城市公共空间的落地载体,是一个自上而下的体系,有外延.可扩展.能适配智慧城市的建设要求.在商业街开展智慧灯杆建设,同期开展5G应用技术试点,有利于商业街 ...

  2. 解决vuex“状态管理调用报错”报错为:"Uncaught ReferenceError: mapactions is not defined"

    报错: 源码: <script> import Vuex from 'vuex'; import {mapActions,mapGetters} from 'vuex'; // conso ...

  3. K Smallest In Unsorted Array

    Find the K smallest numbers in an unsorted integer array A. The returned numbers should be in ascend ...

  4. eureka注册中心增加登录认证

    https://www.cnblogs.com/gxloong/p/12364523.html 开启Eureka注册中心认证   1.目的描述 Eureka自带了一个Web的管理页面,方便我们查询注册 ...

  5. python机器学习——KNN(K近邻算法)

    背景与原理: KNN算法其实是逻辑最简单的分类算法--我们认为一个数据的类型是由与其最接近的数据决定的,而"接近"实际上就是我们度量两个数据点之间的距离,如果我们把一组数据看做一个 ...

  6. 1903021126 申文骏 Java 第六周作业 类与对象

    项目 内容 课程班级博客链接 19级信计班(本) 作业要求链接 第六周作业 博客名称 1903021126  申文骏  Java 第六周作业  类与对象 要求 每道题要有题目,代码(使用插入代码,不会 ...

  7. MAC读写模式自动挂载硬盘/不自动挂载硬盘

    一.卸载硬盘 sudo umount /dev/disk1s1 自己从磁盘工具获取设备ID 或使用终端命令:diskutil list 来获取 二.新建文件夹以供挂载,位子自选 sudo mkdir ...

  8. POJ3723 Conscription 题解

    start: 2021-08-04 16:56:50 题目链接: http://poj.org/problem?id=3723 题目内容: Description Windy has a countr ...

  9. 微信小程序地理定位和城市选择列表

    1.先获取用户是否授权地理定位,如果没有让其跳转到设置页面手动开启(获取到的位置是经纬度,需要借助其他地图SDK的地址逆解析获取省市区的名字) getSetting() { wx.getSetting ...

  10. 服务器安装node

    卸载步骤[未安装请忽略] 1.卸载npm sudo npm uninstall npm -g 2.卸载node yum remove nodejs npm -y 安装步骤 1.下载 wget http ...