简介

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。

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

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);
}

栗子

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

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);}
}

某次输出:

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条件

转载于:https://www.cnblogs.com/zhengbin/p/6420984.html

Java多线程——Condition条件相关推荐

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

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

  2. Java 多线程 —— ReentrantLock 与 Condition

    引言 ReentrantLock 是 JUC 下的一个功能强劲的锁工具,支持公平锁.非公平锁,以及多等待队列的 Condition . 也常常被称为"手动锁".本篇博客主要分析它的 ...

  3. Python并行编程(四):多线程同步之condition(条件变量)实现带有缓冲区的生产者-消费者模型...

    什么是Condtion? 所谓condition条件变量,即这种机制是在满足了特定的条件后,线程才可以访问相关的数据. 这种同步机制就是一个线程等待特定的条件,另一个线程通知它条件已经发生.一旦条件发 ...

  4. java多线程三之线程协作与通信实例

    多线程的难点主要就是多线程通信协作这一块了,前面笔记二中提到了常见的同步方法,这里主要是进行实例学习了,今天总结了一下3个实例: 1.银行存款与提款多线程实现,使用Lock锁和条件Condition. ...

  5. Java多线程发展简史

    摘自: http://www.raychase.net/698 这篇文章,大部分内容,是周五我做的一个关于如何进行Java多线程编程的Knowledge Sharing的一个整理,我希望能对Java从 ...

  6. Java总结篇系列:Java多线程(三)

    2019独角兽企业重金招聘Python工程师标准>>> 本文主要接着前面多线程的两篇文章总结Java多线程中的线程安全问题. 一.一个典型的Java线程安全例子 public cla ...

  7. ***JAVA多线程和并发基础面试问答

    多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一.在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应日后碰到的问题.(校对注:非常赞同这个观 ...

  8. 顶级Java多线程面试题及回答

    1)现在有T1.T2.T3三个线程,你怎样保证T2在T1执行完后执行,T3在T2执行完后执行? 这个线程问题通常会在第一轮或电话面试阶段被问到,目的是检测你对"join"方法是否熟 ...

  9. [转] Java多线程发展简史

    这篇文章,大部分内容,是周五我做的一个关于如何进行Java多线程编程的Knowledge Sharing的一个整理,我希望能对Java从第一个版本开始,在多线程编程方面的大事件和发展脉络有一个描述,并 ...

  10. 阻塞队列的应用 || 多线程的条件判断 一定要用while,而不要用 if

    ProdConsTradiDemo.java package thread;import java.util.concurrent.locks.Condition; import java.util. ...

最新文章

  1. 买了云服务器和域名怎么进行解析
  2. ARM汇编 beq和bne
  3. make file教程(转)
  4. Java21-day12【网络编程(网络编程入门(ip地址、端口、协议、InetAddress)、UDP通信程序、TCP通信程序)】
  5. python 爬虫-beautifulsoup4
  6. 前端学习(2791):实现上拉加载更多
  7. python课设代码_python课程编程题汇总(上)
  8. 为什么只看重结果_买家下单最看重的三项服务,做好这三点,让你的销量涨涨涨...
  9. 高等代数(邱维声):高等代数的研究对象
  10. clustalX2使用以及相关的问题
  11. 计算机英语文体特点,公共英语五级写作文体特点分析
  12. SylixOS -- 双网卡冗余备份设计方案
  13. 毕业好几年,改行学IT哪个岗位容易上手?
  14. github上Android常用第三方库
  15. 计算机是怎样跑起来在线看,计算机是怎样跑起来的
  16. ASp.net判断文件或文件夹是否存在
  17. 对TS流的一些理解TS流的结构
  18. sun.misc.BASE64Encoder详解
  19. 系统安全加固4——输入密码错误5次锁定账户900秒
  20. 第三方分享和登录时assets目录下ShareSDK.xml

热门文章

  1. 短视频追剧的末日来了?
  2. 算法图解 各部分回忆
  3. 转载:HTML5及移动端BUG
  4. Ubuntu下安装sublime text3并汉化
  5. ORACLE 36进制和10进制,互相转换函数
  6. 激烈讨论:我身边的IT认证
  7. 英文写作的25个黄金加分句型
  8. linux远程安装和使用
  9. POJ 3581:Sequence(后缀数组)
  10. poj 2065 SETI(gauss---≡)