Java Concurrency - Condition
Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待池(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
条件(也称为条件队列或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的线程通知它之前,一直挂起该线程(即让其 “等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待池中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length)
putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length)
takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。
注意,Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 wait 和 notification 监视器方法。获取 Condition 实例的监视器锁或者使用其监视器方法,与获取和该 Condition 相关的 Lock 或使用其 waiting 和 signalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 Condition 实例。
Condition 的方法
await()
造成当前线程在接到信号或被中断之前一直处于等待状态。
与此 Condition 相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下四种情况之一 以前,当前线程将一直处于休眠状态:
- 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者
- 其他某个线程调用此 Condition 的 signalAll() 方法;或者
- 其他某个线程中断当前线程,且支持中断线程的挂起;或者
- 发生 “虚假唤醒”
在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。在线程返回时,可以保证它保持此锁。
如果当前线程:
- 在进入此方法时已经设置了该线程的中断状态;或者
- 在支持等待和中断线程挂起时,线程被中断,
则抛出 InterruptedException,并清除当前线程的中断状态。在第一种情况下,没有指定是否在释放锁之前发生中断测试。
awaitNanos(long nanosTimeout)
造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
与此条件相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下五种情况之一以前,当前线程将一直处于休眠状态:
- 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者
- 其他某个线程调用此 Condition 的 signalAll() 方法;或者
- 其他某个线程中断当前线程,且支持中断线程的挂起;或者
- 已超过指定的等待时间;或者
- 发生 “虚假唤醒”。
在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。在线程返回时,可以保证它保持此锁。
如果当前线程:
- 在进入此方法时已经设置了该线程的中断状态;或者
- 在支持等待和中断线程挂起时,线程被中断,
则抛出 InterruptedException,并且清除当前线程的已中断状态。在第一种情况下,没有指定是否在释放锁之前发生中断测试。
在返回时,该方法返回了所剩毫微秒数的一个估计值,以等待所提供的 nanosTimeout 值的时间,如果超时,则返回一个小于等于 0 的值。可以用此值来确定在等待返回但某一等待条件仍不具备的情况下,是否要再次等待,以及再次等待的时间。此方法的典型用法采用以下形式:
synchronized boolean aMethod(long timeout, TimeUnit unit) {
long nanosTimeout = unit.toNanos(timeout);
while (!conditionBeingWaitedFor) {
if (nanosTimeout > 0)
nanosTimeout = theCondition.awaitNanos(nanosTimeout);
else
return false;
}
// ...
}
await(long time, TimeUnit unit)
造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。此方法在行为上等效于:
awaitNanos(unit.toNanos(time)) > 0
awaitUntil(Date deadline)
造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
与此条件相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下五种情况之一以前,当前线程将一直处于休眠状态:
- 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者
- 其他某个线程调用此 Condition 的 signalAll() 方法;或者
- 其他某个线程中断当前线程,且支持中断线程的挂起;或者
- 指定的最后期限到了;或者
- 发生 “虚假唤醒”。
在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。在线程返回时,可以保证 它保持此锁。
如果当前线程:
- 在进入此方法时已经设置了该线程的中断状态;或者
- 在支持等待和中断线程挂起时,线程被中断,
则抛出 InterruptedException,并且清除当前线程的已中断状态。在第一种情况下,没有指定是否在释放锁之前发生中断测试。
返回值指示是否到达最后期限,使用方式如下:
synchronized boolean aMethod(Date deadline) {
boolean stillWaiting = true;
while (!conditionBeingWaitedFor) {
if (stillWaiting)
stillWaiting = theCondition.awaitUntil(deadline);
else
return false;
}
// ...
}
awaitUninterruptibly()
造成当前线程在接到信号之前一直处于等待状态。
与此条件相关的锁以原子方式释放,并且出于线程调度的目的,将禁用当前线程,且在发生以下三种情况之一以前,当前线程将一直处于休眠状态:
- 其他某个线程调用此 Condition 的 signal() 方法,并且碰巧将当前线程选为被唤醒的线程;或者
- 其他某个线程调用此 Condition 的 signalAll() 方法;或者
- 发生“虚假唤醒”
在所有情况下,在此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁。在线程返回时,可以保证它保持此锁。
如果在进入此方法时设置了当前线程的中断状态,或者在等待时线程被中断,那么在接到信号之前,它将继续等待。当最终从此方法返回时,仍然将设置其中断状态。
signal()
唤醒一个等待线程。
如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从 await 返回之前,该线程必须重新获取锁。
signalAll()
唤醒所有等待线程。
如果所有的线程都在等待此条件,则唤醒所有线程。在从 await 返回之前,每个线程都必须重新获取锁。
Java Concurrency - Condition的更多相关文章
- 《Java Concurrency》读书笔记,使用JDK并发包构建程序
1. java.util.concurrent概述 JDK5.0以后的版本都引入了高级并发特性,大多数的特性在java.util.concurrent包中,是专门用于多线并发编程的,充分利用了现代多处 ...
- 深入浅出 Java Concurrency (35): 线程池 part 8 线程池的实现及原理 (3)[转]
线程池任务执行结果 这一节来探讨下线程池中任务执行的结果以及如何阻塞线程.取消任务等等. 1 package info.imxylz.study.concurrency.future;2 3 publ ...
- Java Concurrency in Practice 读书笔记 第十章
粗略看完<Java Concurrency in Practice>这部书,确实是多线程/并发编程的一本好书.里面对各种并发的技术解释得比较透彻,虽然是面向Java的,但很多概念在其他语言 ...
- Java Concurrency - 浅析 CountDownLatch 的用法
The Java concurrency API provides a class that allows one or more threads to wait until a set of ope ...
- Java Concurrency - 浅析 CyclicBarrier 的用法
The Java concurrency API provides a synchronizing utility that allows the synchronization of two or ...
- Java Concurrency - 浅析 Phaser 的用法
One of the most complex and powerful functionalities offered by the Java concurrency API is the abil ...
- Java Concurrency - 线程执行器
Usually, when you develop a simple, concurrent-programming application in Java, you create some Runn ...
- Java Concurrency - Callable & Future
One of the advantages of the Executor framework is that you can run concurrent tasks that return a r ...
- 深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则
转: http://www.blogjava.net/xylz/archive/2010/07/03/325168.html 在这个小结里面重点讨论原子操作的原理和设计思想. 由于在下一个章节中会谈到 ...
随机推荐
- CentOS查看系统信息-CentOS查看命令
一:查看cpu more /proc/cpuinfo | grep "model name" grep "model name" /proc/cpuinfo 如 ...
- 跟Android自带模拟器说拜拜,Mac Genymotion 使用心得
今天看到网上一片文章点击打开链接,很是激动,套用原作者的话,性能卓越作为历史上最快的Android模拟器(没有之一),秒级开机关机速度足够让你膜拜了(粗略估计5-20s不等),我的Mac上面运行And ...
- 用C#调用蓝牙编程
2013-04-22 09:41:06 什么是蓝牙? 现在只能手机这么发达,蓝牙对我们来说肯定不陌生.我来介绍一下官方概念: 蓝牙,是一种支持设备短距离通信(一般10m内)的无线电技术.能在包括移动电 ...
- 在VS中安装EF和项目引用EF
1.通过Visual Studio安装NuGet (1). 打开Visual Studio扩展管理器 (2). 选择联机库,并在搜索中写入NuGet,然后点击搜索结果中NuGet Packag ...
- PostgreSQL的 initdb 源代码分析之二十五
继续分析: make_postgres(); 展开: 目的是创建postgres数据库. cmd是:/home/pgsql/project/bin/postgres" --single -F ...
- skyline TerraBuilder 制作MPT方法与技巧(1)
MPT是skyline独有的三维地形数据格式,可简单理解为 影像图+高程=三维地形(三维底图),以下介绍用skyline TerraBuilder(以下简称TB)制作MPT的方法与技巧 用TB制作MP ...
- delphi TToolBar
工具栏 的属性 xe4的事件 Customizable OnCustomizeCanDelete OnCustomizeCanInsert OnCustomized OnCustomizeAdded ...
- 以C#编写的Socket服务器的Android手机聊天室Demo
内容摘要 1.程序架构 2.通信协议 3.服务器源代码 4.客户端源代码 5.运行效果 一.程序架构 在开发一个聊天室程序时,我们可以使用Socket.Remoting.WCF这些具有双向通信的协议或 ...
- [IOS]自定义长触屏事件
写一个Demo来自定义一个长触屏事件,自定义长按手势. 实现步骤: 1.创建一个自定义手势类,命名为LongPressGestureRecognizer,在创建的时候继承UIGestureRecogn ...
- 设置EXCEL2010打开多个独立窗口
最近发现一个奇怪的问题,发现office中的word和ppt在我使用笔记本分屏幕(双屏)的时候都可以将2份文档分别在2个窗口打开,但是在使用excel的时候却发现不行,最后研究发现原因 ...