Condition控制线程通信

  一、前言

  java.util.concurrent.locks.Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个Lock 可能与多个Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的Object 版本中的不同。

  在Condition 对象中,与wait、notify 和notifyAll 方法对应的分别是await、signal 和signalAll。

  Condition 实例实质上被绑定到一个锁上。要为特定Lock 实例获得Condition 实例,请使用其newCondition() 方法。

  Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个wait-sets。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。

  Conditions(也称为条件队列 或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁与该条件相关联。等待提供一个条件的主要属性是:以原子方式 释放相关的锁,并挂起当前线程,就像 Object.wait 做的那样。

  下面看一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们可以在单独的wait-sets中保存 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();
}
}
}

  类似的还有下面的这个生产者和消费者案例

 package me.concurrent.copypac;

 import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /*
* 生产者消费者案例:
*/
public class TestProductorAndConsumerForLock { public static void main(String[] args) {
Clerk clerk = new Clerk(); Productor pro = new Productor(clerk);
Consumer con = new Consumer(clerk); new Thread(pro, "生产者 A").start();
new Thread(con, "消费者 B").start(); // new Thread(pro, "生产者 C").start();
// new Thread(con, "消费者 D").start();
} } class Clerk {
private int product = 0; private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition(); // 进货
public void get() {
lock.lock();
try {
if (product >= 1) { // 为了避免虚假唤醒,应该总是使用在循环中。
System.out.println("产品已满!"); try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + ++product);
condition.signalAll();
} finally {
lock.unlock();
}
} // 卖货
public void sale() {
lock.lock();
try {
if (product <= 0) {
System.out.println("缺货!"); try {
condition.await();
} catch (InterruptedException e) {
}
}
System.out.println(Thread.currentThread().getName() + " : " + --product); condition.signalAll();
} finally {
lock.unlock();
}
}
} // 生产者
class Productor implements Runnable { private Clerk clerk; public Productor(Clerk clerk) {
this.clerk = clerk;
} @Override
public void run() {
for (int i = 0; i < 20; i++) {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
clerk.get();
}
}
} // 消费者
class Consumer implements Runnable { private Clerk clerk; public Consumer(Clerk clerk) {
this.clerk = clerk;
} @Override
public void run() {
for (int i = 0; i < 20; i++) {
clerk.sale();
}
} }

  Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。

  注意Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 wait 和 notification 监视器方法。获取 Condition 实例的监视器锁或者使用其监视器方法,与获取和该 Condition 相关的 Lock 或使用其 waiting 和 signalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 Condition 实例。

  除非另行说明,否则为任何参数传递 null 值将导致抛出 NullPointerException。  

  使用Condition时需要注意的问题  

  Condition允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

  三种形式的条件等待(可中断、不可中断和超时)在一些平台上的实现以及它们的性能特征可能会有所不同。尤其是它可能很难提供这些特性和维护特定语义,比如排序保证。更进一步地说,中断线程实际挂起的能力在所有平台上并不是总是可行的。

  因此,并不要求某个实现为所有三种形式的等待定义完全相同的保证或语义,也不要求其支持中断线程的实际挂起。

  要求实现清楚地记录每个等待方法提供的语义和保证,在某个实现不支持中断线程的挂起时,它必须遵从此接口中定义的中断语义。

  由于中断通常意味着取消,而又通常很少进行中断检查,因此实现可以先于普通方法的返回来对中断进行响应。即使出现在另一个操作后的中断可能会释放线程锁时也是如此。实现应记录此行为。

二、Condition中的方法

 void await()
          造成当前线程在接到信号或被中断之前一直处于等待状态。
 boolean await(long time,
TimeUnit unit)


          造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
 long awaitNanos(long nanosTimeout)

          造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
 void awaitUninterruptibly()

          造成当前线程在接到信号之前一直处于等待状态。
 boolean awaitUntil(Date deadline)

          造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
 void signal()

          唤醒一个等待线程。
 void signalAll()

          唤醒所有等待线程。

如果,您对我的这篇博文有什么疑问,欢迎评论区留言,大家互相讨论学习。
如果,您认为阅读这篇博客让您有些收获,不妨点击一下右下角的【推荐】。
如果,您希望更容易地发现我的新博客,不妨点击一下左下角的【关注我】。
如果,您对我的博文感兴趣,可以关注我的后续博客,我是【AlbertRui】。

转载请注明出处和链接地址,欢迎转载,谢谢!

Condition控制线程通信的更多相关文章

  1. Java核心知识点学习----使用Condition控制线程通信

    一.需求 实现线程间的通信,主线程循环3次后,子线程2循环2次,子线程3循环3次,然后主线程接着循环3次,如此循环3次. 即:A->B->C---A->B->C---A-> ...

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

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

  3. 10. Condition 控制线程通信

    1. 是什么 ? 2. 示例 package com.gf.demo09; import java.util.concurrent.locks.Condition; import java.util. ...

  4. Java---Condition控制线程通信

    java中控制线程通信的方法有:1.传统的方式:利用synchronized关键字来保证同步,结合wait(),notify(),notifyAll()控制线程通信.不灵活. 2.利用Conditio ...

  5. Java -- 使用阻塞队列(BlockingQueue)控制线程通信

    BlockingQueeu接口是Queue的子接口,但是它的主要作用并不是作为容器,而是作为线程同步的工具. 特征: 当生产者线程试图向BlockingQueue中放入元素时,如果该队列已满,则该线程 ...

  6. 6.显示锁Lock 和 线程通信Condition

    显示锁 Lock 一.用于解决多线程 安全问题的方式: synchronized:   1.同步代码块      2.同步方法 jdk1.5 后:第三种:同步锁Lock  (注意:同步(synchro ...

  7. Java-多线程第三篇3种创建的线程方式、线程的生命周期、线程控制、线程同步、线程通信

    1.Java使用Thread类代表线程.     所有的线程对象必须是Thread类或其子类的实例. 当线程继承Thread类时,直接使用this即可获取当前线程,Thread对象的getName() ...

  8. Java 线程通信

    线程通信用来保证线程协调运行,一般在做线程同步的时候才需要考虑线程通信的问题. 1.传统的线程通信 通常利用Objeclt类提供的三个方法: wait() 导致当前线程等待,并释放该同步监视器的锁定, ...

  9. JAVA基础知识之多线程——线程通信

    传统的线程通信 Object提供了三个方法wait(), notify(), notifyAll()在线程之间进行通信,以此来解决线程间执行顺序等问题. wait():释放当前线程的同步监视控制器,并 ...

随机推荐

  1. SQLyog 破解版

    百度云:链接:http://pan.baidu.com/s/1eSMEzIE    密码:ubi2

  2. 自动生成Mybatis的Mapper文件

    自动生成Mybatis的Mapper文件 工作中使用mybatis时我们需要根据数据表字段创建pojo类.mapper文件以及dao类,并且需要配置它们之间的依赖关系,这样的工作很琐碎和重复,myba ...

  3. 准备开始,选好第一个C#的封装库

    如今C#做工业图像处理和开发,最多资料和例子的就是Emgu.不过现在人家开始商业收费了,对于我们这些小企业就不是很好了.这里,我要介绍和推荐的是另外一个也同样牛逼的C#封装Opnecv的库,叫做Ope ...

  4. go mod 无法自动下载依赖包的问题

    go 11以后启用了go mod功能,用于管理依赖包. 当执行go mod init生成go.mod文件之后,golang在运行.编译项目的时候,都会检查依赖并下载依赖包. 在启动了go mod之后, ...

  5. Spark 源码分析系列

    如下,是 spark 源码分析系列的一些文章汇总,持续更新中...... Spark RPC spark 源码分析之五--Spark RPC剖析之创建NettyRpcEnv spark 源码分析之六- ...

  6. nginx 301跳转https后post请求失效问题解决

    app本地请求是http端口,后来升级https强制301跳转,设置如下 server { listen 80; server name www.XXX.com; rewrite ^/(.*)$ ht ...

  7. 【iOS】安装 CocoaPods

    1. 打开 terminal 2. 移除现有 Ruby 默认源 $ gem sources --remove https://rubygems.org/ 3. 使用新的源 $ gem sources ...

  8. Python—三目运算

    Python 可通过 if 语句来实现三目运算的功能,因此可以近似地把这种 if 语句当成三目运算符.作为三目运算符的 if 语句的语法格式如下: (True_statements) if (expr ...

  9. JAVA开始(基础篇)

    数据类型 Boolean        1位Byte              1个字节(8位)Short             2个字节Char              2个字节Int      ...

  10. Hadoop 系列(一)—— 分布式文件系统 HDFS

    一.介绍 HDFS (Hadoop Distributed File System)是 Hadoop 下的分布式文件系统,具有高容错.高吞吐量等特性,可以部署在低成本的硬件上. 二.HDFS 设计原理 ...