简介

Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。

不同的是,Object中的wait(),notify(),notifyAll()方法是和"同步锁"(synchronized关键字)捆绑使用的;而Condition是需要与"互斥锁"/"共享锁"捆绑使用的。

简单应用:

Condition的实现分析

Condition是同步器AbstractQueuedSynchronized的内部类,因为Condition的操作需要获取相关的锁,所以作为同步器的内部类比较合理。每个Condition对象都包含着一个队列(等待队列),该队列是Condition对象实现等待/通知功能的关键。

等待队列:

等待队列是一个FIFO的队列,队列的每一个节点都包含了一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用了await()方法,该线程就会释放锁、构造成节点进入等待队列并进入等待状态。

这里的节点定义也就是AbstractQueuedSynchronizer.Node的定义。

一个Condition包含一个等待队列,Condition拥有首节点(firstWaiter)和尾节点(lastWaiter)。当前线程调用Condition.await()方法时,将会以当前线程构造节点,并将节点从尾部加入等待队列。

在Object的监视器模型上,一个对象拥有一个同步队列和等待队列,而Lock(同步器)拥有一个同步队列和多个等待队列。

等待(await):

调用Condition的await()方法,会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。

从队列的角度来看,相当于同步队列的首节点(获取了锁的节点)移动到Condition的等待队列中。

当等待队列中的节点被唤醒,则唤醒节点的线程开始尝试获取同步状态。如果不是通过Condition.signal()方法唤醒,而是对等待线程进行中断,则抛出InterruptedException。

+ View code
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 添加至等待队列中
Node node = addConditionWaiter();
// 释放同步状态,释放锁
long savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}

通知(signal):

调用Condition的signal()方法,将会唤醒在等待队列中从首节点开始搜索未解除Condition的节点,在唤醒节点之前,会将节点移到同步队列中。

Condition的signalAll()方法,相当于对等待队列中的每个节点均执行一次signal()方法,将等待队列中的节点全部移动到同步队列中,并唤醒每个节点的线程。

+ View code
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
} public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}

栗子

经典问题,消费者/生产者:

+ View code
package ConsumerAndProduce;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* Created by zhengbinMac on 2017/2/20.
*/
class Depot {
private int capacity;
private int size;
private Lock lock;
private Condition consumerCond;
private Condition produceCond; public Depot(int capacity) {
this.capacity = capacity;
this.size = 0;
this.lock = new ReentrantLock();
this.consumerCond = lock.newCondition();
this.produceCond = lock.newCondition();
} public void produce(int val) {
lock.lock();
try {
int left = val;
while (left > 0) {
while (size >= capacity) {
produceCond.await();
}
int produce = (left+size) > capacity ? (capacity-size) : left;
size += produce;
left -= produce;
System.out.println(Thread.currentThread().getName() + ", ProduceVal=" + val + ", produce=" + produce + ", size=" + size);
consumerCond.signalAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
} public void consumer(int val) {
lock.lock();
try {
int left = val;
while (left > 0) {
while (size <= 0) {
consumerCond.await();
}
int consumer = (size <= left) ? size : left;
size -= consumer;
left -= consumer;
System.out.println(Thread.currentThread().getName() + ", ConsumerVal=" + val + ", consumer=" + consumer + ", size=" + size);
produceCond.signalAll();
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
class Consumer {
private Depot depot;
public Consumer(Depot depot) {
this.depot = depot;
} public void consumerThing(final int amount) {
new Thread(new Runnable() {
public void run() {
depot.consumer(amount);
}
}).start();
}
}
class Produce {
private Depot depot;
public Produce(Depot depot) {
this.depot = depot;
} public void produceThing(final int amount) {
new Thread(new Runnable() {
public void run() {
depot.produce(amount);
}
}).start();
}
}
public class Entrepot {
public static void main(String[] args) {
// 仓库
Depot depot = new Depot(100);
// 消费者
Consumer consumer = new Consumer(depot);
// 生产者
Produce produce = new Produce(depot);
produce.produceThing(5);
consumer.consumerThing(5);
produce.produceThing(2);
consumer.consumerThing(5);
produce.produceThing(3);
}
}

某次输出:

+ View code
Thread-0, ProduceVal=5, produce=5, size=5
Thread-1, ConsumerVal=5, consumer=5, size=0
Thread-2, ProduceVal=2, produce=2, size=2
Thread-3, ConsumerVal=5, consumer=2, size=0
Thread-4, ProduceVal=3, produce=3, size=3
Thread-3, ConsumerVal=5, consumer=3, size=0

输出结果中,Thread-3出现两次,就是因为要消费5个产品,但仓库中只有2个产品,所以先将库存的2个产品全部消费,然后这个线程进入等待队列,等待生产,随后生产出了3个产品,生产者生产后又执行signalAll方法将等待队列中所有的线程都唤醒,Thread-3继续消费还需要的3个产品。

参考资料:

《Java并发编程的艺术》 - 5.6 Condition接口

Java多线程系列--“JUC锁”06之 Condition条件

Java多线程——Condition条件的更多相关文章

  1. java多线程-Condition

    Condition 将 Object 监视器方法(wait.notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set ...

  2. java多线程 -- Condition 控制线程通信

    Api文档如此定义: Condition 将 Object 监视器方法(wait.notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对 ...

  3. Java多线程Condition定点通知

    多线程之间按顺序调用,实现A->B->C三个线程启动,要求如下:A打印5次,B打印10次,C打印15次接着 A打印5次,B打印10次,C打印15次 来10轮 package com.yan ...

  4. python多线程--Condition(条件对象)

    Condition class threading.Condition(lock=None 这个类实现条件变量对象.条件变量允许一个或多个线程等待,知道它们被另一个线程唤醒. 如果给出了lock参数而 ...

  5. java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)

    Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法 可以理解为,原本借助于synchronized关键字以及锁对象,配备了 ...

  6. Java多线程系列--“JUC锁”06之 Condition条件

    概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition介绍Condition函数列表Condition示例转载请注明出处 ...

  7. Java多线程(九)之ReentrantLock与Condition

    一.ReentrantLock 类   1.1 什么是reentrantlock   java.util.concurrent.lock 中的 Lock 框架是锁定的一个抽象,它允许把锁定的实现作为 ...

  8. Java并发(十一):Condition条件

    先做总结: 1.为什么使用Condition条件? synchronized配合Object的wait().notify()系列方法可以实现等待/通知模式. Lock提供了条件Condition,对线 ...

  9. java 多线程(三)条件对象

    转载请注明出处:http://blog.csdn.net/xingjiarong/article/details/47417383 在上一篇博客中,我们学会了用ReentrantLock来控制线程訪问 ...

随机推荐

  1. 2018-2019-2 20175313 实验一《Java开发环境的熟悉》实验报告

    一.实验内容及步骤 使用JDK编译.运行简单的Java程序 cd code进入code文件夹 mkdir 20175313创建20175313文件夹 ls查看当前目录 cd 20175313,mkdi ...

  2. 002-自定义打开terminal,以及快捷键,其他程序类似,ssh管理-sshpass, Shuttle

    一.利用Automator软件完成服务设定 1.使用Command+Space,打开Spotlight,搜索Automator 2.搜索到之后,双击打开,选择“服务[或快速操作]” 3.将“服务收到[ ...

  3. python连接mysql-PyMySql模块

    安装 pip3 install pymysql 使用 输出mysql版本 import pymysql # 打开数据库连接 db = pymysql.connect("localhost&q ...

  4. Git环境配置

    1,下载Git-2.16.2-64-bit.exe并安装, 全部为默认设置 下载地址:http://git-scm.com/download/win 2 在开始菜单中,单击Git CMD,执行下面命令 ...

  5. date_default_timezone_set()问题解决方案(PHP5.3以上的)

      date() [<a href='function.date'>function.date</a>]: It is not safe to rely on the syst ...

  6. C语言进阶之路(二)----字符串操作常见模型

    1.while模型 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #includ ...

  7. linux----------今天又遇到一个奇葩的问题,就是linux文件的权限已经是777了但是还是没有写入权限,按照下面的命令就解决了

    查看SELinux状态: 1./usr/sbin/sestatus -v  ##如果SELinux status参数为enabled即为开启状态 SELinux status:             ...

  8. Java中try、finally语句中有return时的执行情况 [转]

    原文:http://kingj.iteye.com/blog/1436761 在Java中当try.finally语句中包含return语句时,执行情况到底是怎样的,finally中的代码是否执行,大 ...

  9. html5 javascript 事件练习3随机键盘

    <!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8&qu ...

  10. Hdu2191 悼念512汶川大地震遇难同胞——珍惜现在,感恩生活 (多重背包)

    Problem Description 急!灾区的食物依然短缺!为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品, ...