简介

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
  1. public final void await() throws InterruptedException {
  2. if (Thread.interrupted())
  3. throw new InterruptedException();
  4. // 添加至等待队列中
  5. Node node = addConditionWaiter();
  6. // 释放同步状态,释放锁
  7. long 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) // clean up if cancelled
  17. unlinkCancelledWaiters();
  18. if (interruptMode != 0)
  19. reportInterruptAfterWait(interruptMode);
  20. }

通知(signal):

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

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

+ View code
  1. public final void signal() {
  2. if (!isHeldExclusively())
  3. throw new IllegalMonitorStateException();
  4. Node first = firstWaiter;
  5. if (first != null)
  6. doSignal(first);
  7. }
  8.  
  9. public final void signalAll() {
  10. if (!isHeldExclusively())
  11. throw new IllegalMonitorStateException();
  12. Node first = firstWaiter;
  13. if (first != null)
  14. doSignalAll(first);
  15. }

栗子

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

+ View code
  1. package ConsumerAndProduce;
  2.  
  3. import java.util.concurrent.locks.Condition;
  4. import java.util.concurrent.locks.Lock;
  5. import java.util.concurrent.locks.ReentrantLock;
  6.  
  7. /**
  8. * Created by zhengbinMac on 2017/2/20.
  9. */
  10. class Depot {
  11. private int capacity;
  12. private int size;
  13. private Lock lock;
  14. private Condition consumerCond;
  15. private Condition produceCond;
  16.  
  17. public Depot(int capacity) {
  18. this.capacity = capacity;
  19. this.size = 0;
  20. this.lock = new ReentrantLock();
  21. this.consumerCond = lock.newCondition();
  22. this.produceCond = lock.newCondition();
  23. }
  24.  
  25. public void produce(int val) {
  26. lock.lock();
  27. try {
  28. int left = val;
  29. while (left > 0) {
  30. while (size >= capacity) {
  31. produceCond.await();
  32. }
  33. int produce = (left+size) > capacity ? (capacity-size) : left;
  34. size += produce;
  35. left -= produce;
  36. System.out.println(Thread.currentThread().getName() + ", ProduceVal=" + val + ", produce=" + produce + ", size=" + size);
  37. consumerCond.signalAll();
  38. }
  39. } catch (InterruptedException e) {
  40. e.printStackTrace();
  41. } finally {
  42. lock.unlock();
  43. }
  44. }
  45.  
  46. public void consumer(int val) {
  47. lock.lock();
  48. try {
  49. int left = val;
  50. while (left > 0) {
  51. while (size <= 0) {
  52. consumerCond.await();
  53. }
  54. int consumer = (size <= left) ? size : left;
  55. size -= consumer;
  56. left -= consumer;
  57. System.out.println(Thread.currentThread().getName() + ", ConsumerVal=" + val + ", consumer=" + consumer + ", size=" + size);
  58. produceCond.signalAll();
  59. }
  60. } catch (InterruptedException e) {
  61. e.printStackTrace();
  62. } finally {
  63. lock.unlock();
  64. }
  65. }
  66. }
  67. class Consumer {
  68. private Depot depot;
  69. public Consumer(Depot depot) {
  70. this.depot = depot;
  71. }
  72.  
  73. public void consumerThing(final int amount) {
  74. new Thread(new Runnable() {
  75. public void run() {
  76. depot.consumer(amount);
  77. }
  78. }).start();
  79. }
  80. }
  81. class Produce {
  82. private Depot depot;
  83. public Produce(Depot depot) {
  84. this.depot = depot;
  85. }
  86.  
  87. public void produceThing(final int amount) {
  88. new Thread(new Runnable() {
  89. public void run() {
  90. depot.produce(amount);
  91. }
  92. }).start();
  93. }
  94. }
  95. public class Entrepot {
  96. public static void main(String[] args) {
  97. // 仓库
  98. Depot depot = new Depot(100);
  99. // 消费者
  100. Consumer consumer = new Consumer(depot);
  101. // 生产者
  102. Produce produce = new Produce(depot);
  103. produce.produceThing(5);
  104. consumer.consumerThing(5);
  105. produce.produceThing(2);
  106. consumer.consumerThing(5);
  107. produce.produceThing(3);
  108. }
  109. }

某次输出:

+ View code
  1. Thread-0, ProduceVal=5, produce=5, size=5
  2. Thread-1, ConsumerVal=5, consumer=5, size=0
  3. Thread-2, ProduceVal=2, produce=2, size=2
  4. Thread-3, ConsumerVal=5, consumer=2, size=0
  5. Thread-4, ProduceVal=3, produce=3, size=3
  6. 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. 关于maven-resources-plugin配置的隐藏的坑

    昨天发现一个问题, 一个第三方证书的文件存放于resources文件夹下,在本地环境使用该证书进行加密调用第三方接口,没有任何问题,但是发布到测试环境和生产环境(linux)报错,提示证书工厂无法初始 ...

  2. XP支持AHCI硬盘工作模式

    故障 装XP系统后开启AHCI模式会出现开机蓝屏重启的问题,如何在XP下加载AHCI驱动,以便开启BIOS中AHCI选项来发挥硬盘的最佳性能. 问题分析XP系统无法直接支持AHCI硬盘高速模式,需要加 ...

  3. ubuntu12.04下编译Linux tina 2.1/android经验

    用的是osboxes下的vdi. 编译Linux 1. 不能在root用户下操作 2. 执行 make kernel_menuconfig 报错,需要 apt-get install zlib1g z ...

  4. 运行pytorch代码遇到的error解决办法

    1.no CUDA-capable device is detected 首先考虑的是cuda的驱动问题,查看gpu显示是否正常,然后更新最新的cuda驱动: 第二个考虑的是cuda设备的默认参数是否 ...

  5. css3动态计算元素的高度及宽度

    1.px     像素,我们在网页布局中一般都是用px. 2.百分比     百分比一般宽泛的讲是相对于父元素,自适应网页布局越来越多,百分比也经常用到了 3.Viewport 当已知一个div的高度 ...

  6. JSP 修改不能编辑

    JSP做修改功能时候,有的时候,某些值要设置成只读状态,不能修改,刚开始做的时候,出现了修改之后值传不到后台的情况,由于刚出来工作不久,不是很了解这个.思索了半天,才发现是由于这个属性的缘故.浪费了大 ...

  7. 01-python3.5-模块导入-while-for-range-break-continue

    一.输入用户名和密码----导入getpass模块 #!/usr/bin/env python # -*- coding:utf-8 -*- #Author:XZ """ ...

  8. Python 第五阶段 学习记录之----rabbmit

    消息服务器rabbmit RabbitMQ 消息队列 python里有两个Q, threading queue.不同线程间数据交互 进程Queue: 不同进程间交互这个说法是错误的. 这个是用于父进程 ...

  9. MTCNN试用

    检测工作想借用MTCNN里的48-net,源码来自CongWeilin Git 下下来就能跑,真是良心 进入pepare_data准备好数据以后进入48-net,目录下有一个pythonLayer.p ...

  10. firefox 实现web交互机器人

    现在仅有火狐浏览器可以这样操作 -- Filefox 下面是项目目录 -- 前端页面 -- html <!DOCTYPE html> <html lang="en" ...