Condition 接口与 Lock 配合实现了等待 / 通知模式,这个和 Object 的监视器方法(wait、notify、notifyAll 等方法)一样,都是实现了等待 / 通知模式,但这两者在使用的方式以及功能特性上还是有差别的。

Object 的监视器方法与 Condition 接口的对比
对比项 Object Monitor Methods Condition
前置条件 获取对象的锁

调用Lock.lock()获取锁

调用Lock.newCondition()获取Condition对象

调用方式 直接调用,如:Object.wait() 直接调用,如:condition.await()
等待队列个数 一个 多个
当前线程释放锁并进入等待状态 支持 支持

当前线程释放锁并进入等待状态,

在等待状态中不响应中断

不支持 支持
当前线程释放锁并进入等待超时状态 支持 支持
当前现场释放锁并进入等待状态到将来某个时间 不支持 支持
唤醒等待队列中的一个线程 支持 支持
唤醒等待队列中的所有线程 支持 支持

在 Object 监视模型中,一个对象有一个同步队列和一个等待队列,而 AQS 拥有一个同步队列和多个等待队列。

Object 的监视模型:

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

Condition 部分方法以及描述:

方法名称 描述
void await() throws
InterruptedException

当前线程进入等待状态,直到被通知(signal)或中断,

当前线程才会进入运行状态且从此await方法返回(表明

该线程已经获取了Condition对象对应的锁),包括:

其他线程(调用了interrupt()方法中断了当前线程)调用

该 Condition 的 signal() 或 signalAll()方法,而当前线

程被选中唤醒

void awaitUninterruptibly()
当前线程进入等待状态,知道被通知,对中断不敏感
long awaitNanos(long nanosTimeout)
throws InterruptedException

当前线程进入等待状态直到被通知、中断或者是超时,

返回值代表剩余的时间,如果在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接口的更多相关文章

  1. Java并发Condition接口

    java.util.concurrent.locks.Condition接口提供一个线程挂起执行的能力,直到给定的条件为真. Condition对象必须绑定到Lock,并使用newCondition( ...

  2. JAVA并发-Condition

    简介 在没有Lock之前,我们使用synchronized来控制同步,配合Object的wait().notify()系列方法可以实现等待/通知模式.在Java SE5后,Java提供了Lock接口, ...

  3. 在Java的Condition接口【唤醒全部线程】

    在Java的Condition接口中,存在的几个方法跟Synchronized中的wait(),waitall(),wait(time ^),这个几个方法一一对应起来,但是在Lock.newCondi ...

  4. Java并发ReadWriteLock接口

    java.util.concurrent.locks.ReadWriteLock接口允许一次读取多个线程,但一次只能写入一个线程. 读锁 - 如果没有线程锁定ReadWriteLock进行写入,则多线 ...

  5. Java并发Lock接口

    java.util.concurrent.locks.Lock接口用作线程同步机制,类似于同步块.新的锁定机制更灵活,提供比同步块更多的选项. 锁和同步块之间的主要区别如下: 序列的保证 - 同步块不 ...

  6. Java并发AtomicLong接口

    java.util.concurrent.atomic.AtomicLong类提供了可以被原子地读取和写入的底层long值的操作,并且还包含高级原子操作. AtomicLong支持基础long类型变量 ...

  7. Java 并发编程之 Condition 接口

    本文部分摘自<Java 并发编程的艺术> 概述 任意一个 Java 对象,都拥有一个监视器方法,主要包括 wait().wait(long timeout).notify() 以及 not ...

  8. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  9. Java的LockSupport工具,Condition接口和ConditionObject

    在之前我们文章(关于多线程编程基础和同步器),我们就接触到了LockSupport工具和Condition接口,之前使用LockSupport工具来唤醒阻塞的线程,使用Condition接口来实现线程 ...

随机推荐

  1. Java实现发送邮件,图片,附件

    参照地址 1.JavaMail 介绍 JavaMail 是sun公司(现以被甲骨文收购)为方便Java开发人员在应用程序中实现邮件发送和接收功能而提供的一套标准开发包,它支持一些常用的邮件协议,如前面 ...

  2. 【转载】linux 工作队列上睡眠的认识--不要在默认共享队列上睡眠

    最近项目组做xen底层,我已经被完爆无数遍了,关键在于对内核.驱动这块不熟悉,导致分析xen代码非常吃力.于是准备细细的将 几本 linux 书籍慢慢啃啃. 正好看到LINUX内核设计与实现,对于内核 ...

  3. redis的集群安装

    1.创建安装目录 在master ,node1 ,node2中分别创建 mkdir /usr/local/soft/redis-cluster 2.将redis 复制到redis-cluster 目录 ...

  4. 抽奖之Flash大转盘

    1.搭建JS与Flash互通的环境 function thisMovie(movieName){ if (window.document[movieName]) { return window.doc ...

  5. 【Python学习】print语句

    一.print 可以向屏幕上输出信息,print 后面一个空格再加上''中间放入要输出的内容. 二.print可以用逗号分隔语句,但是每有一个逗号就会出来一个空格. 1 >>> pr ...

  6. 深入学习Composer原理(一)

    Composer作为PHP的包管理工具,为PHPer们提供了丰富的类库,并且让PHP重焕新生,避免被时代淘汰的悲剧.可以说,Composer和PHP7是现在PHP开发者的标配,如果你还没用过Compo ...

  7. jquery .play()报错is not a function

    报错原因:play()方法属于DOM对象方法,$('#audio')为jquery对象解决办法:将jquery对象转换为DOM对象首先打印jquery对象$('#audio') 两种转换方式将一个jQ ...

  8. centos linux服务器apache+mysql环境访问慢优化方法

    查找软件安装目录:find / -name 软件名称 一.优化apache配置增加MaxClients的值 默认情况下,2.0及以上apache版本MaxClients的值为256,对于中大型应用访问 ...

  9. Orchard Core Framework Samples

    解决方案包含内容 多租户应用 一个ASP.NET Core MVC应用程序,它引用模块项目,并为两个启用了不同模块的租户提供支持. 此Web应用程序的主页提供了更多信息,并链接到两个租户和模块端点.租 ...

  10. 51nod1600-Simple KMP【SAM,树链剖分】

    正题 题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=1600 题目大意 给出一个字符串\(s\),每次在最后插入一个字符后求它的 ...