java并发学习--第六章 线程之间的通信
一、等待通知机制wait()与notify()
在线程中除了线程同步机制外,还有一个最重要的机制就是线程之间的协调任务。比如说最常见的生产者与消费者模式,很明显如果要实现这个模式,我们需要创建两个线程,一个生产者,一个消费者;有两个线程还不够,如果当生产者生产商品完成后,消费者如何知道要去消费生产的商品?为此JDK给我们可提供了wait()和notify()方法来进行线程之间的通信,从而解决了生产者完成后通知消费者进行消费的问题。
wait()和notify()是等待/唤醒机制,调用wait()方法可以让当前线程阻塞,只有当使用notify()时,才能让阻塞的线程继续执行。这样的机制就能够达道两个线程之间通信的功能。
我们来看看这两个方法:
1.wait()、notify()和notifyAll()方法是都是Objec类中自带的方法,因此所有的类都能够使用;
2.wait()、notify()和notifyAll()是native方法,无法被重写;
3.调用某个对象的wait()方法能让当前线程阻塞,并且当前线程必须拥有此对象的锁;
4.notify()方法能够唤醒一个正在等待的线程,如果有多个线程都在等待被唤醒,调用notify()方法只能随机唤醒一个方法;
5.调用notifyAll()方法能够唤醒所有正在等待的线程;
6.使用wait()、notify()和notifyAll()方法必须给线程加锁。
我们用一个例子来介绍生成者与消费者模式:
生产者代码:
/**
* 生产者,生成了10个商品后通知消费者消费
*/
public class Productor implements Runnable { @Override
public void run() {
//注意,这里生成者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
synchronized (Job.value) {
int i = 0;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (i < 10) {
//生产任务
Job.value[i] = "商品:" + i;
System.out.println("开始生产" + Job.value[i]);
i++;
}
//生产完成后,唤醒所有线程,让消费者消费
System.out.println("生产完成,开始消费");
Job.value.notifyAll();
}
}
}
消费者代码:
/**
* 消费者,等待生产者生成完商品后,才开始消费
*/
public class Consume implements Runnable { @Override
public void run() {
//注意,这里生产者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
synchronized (Job.value) {
//先判断有没商品生产,如果没有则开始等到生产者生成
if (Job.value[0] == null) {
try {
//等待生产
System.out.println("当前没有任务,等到生产");
Job.value.wait(0);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产完成后,唤醒消费者后面的线程任务,消费商品
for (String string : Job.value) {
System.out.println("消费"+string);
}
} }
}
测试类:
public class Job {
public static String value[]=new String[10] ;
public static void main(String[] args) {
Productor productor=new Productor();
Consume consume=new Consume();
new Thread(consume).start();
new Thread(productor).start();
}
}
运行的结果:

二、ReentrantLock中的等待唤醒机制Condition类
前面我们已经介绍了JDK中object自带的wait()与notify()方法,但是notify()在多线程环境下只能随机唤醒一个线程,这样的功能实在是有些鸡肋,为此在AQS(AbstractQueuedSynchronizer)中,为我们提供了一个Condition类实现了等待唤醒机制,并且Condition中唤醒的机制更加灵活。
Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比使用Object的wait()、notify(),使用Condition1的await()、signal()这种方式实现线程间协作更加安全和高效。因此通常来说比较推荐使用Condition。
Condition中常用的是await()和signal()方法,与wait()与notify一样,必须获取同一把锁才能实现等待唤醒机制,接下来我们就使用Condition中await()和signal()方法对我们的生产者和消费者模式进行改造:再增加一个消费者2,如果商品队列为空,两个消费者都进行等待,当生产者生产完成后唤醒消费者1,消费者1消费完成后唤醒消费者2,这样就能够指定唤醒一个线程
改造后的生成者:
/**
* 生产者,生成了10个商品后唤醒所有线程
*/
public class Productor implements Runnable {
//获取的锁
Lock lock;
//生成者和消费者1使用的等待/通知对象,用于绑定生成者和消费者1的等待/通知关系
Condition condition; public Productor(Lock lock, Condition condition) {
this.lock = lock;
this.condition = condition;
} @Override
public void run() {
//注意,这里生成者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
lock.lock();
int i = 0;
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
while (i < 5) {
//生产任务
Job.value[i] = "商品:" + i;
System.out.println("开始生产" + Job.value[i]);
i++;
}
//生产完成后,让消费者1消费
System.out.println("生产完成,开始消费");
condition.signal();
//必须要释放锁
lock.unlock();
}
}
改造后的消费者1:
/**
* 消费者1,等待生产者生成完商品后,才开始消费
*/
public class Consume implements Runnable {
//获取的锁
Lock lock;
//生成者和消费者1使用的等待/通知对象,用于绑定生成者和消费者1的等待/通知关系
Condition condition;
//消费者1和消费者2使用的等待/通知对象,用于绑定消费者1和消费者2的等待/通知关系
Condition consumeCondition; public Consume(Lock lock, Condition condition, Condition consumeCondition) {
this.lock = lock;
this.condition = condition;
this.consumeCondition = consumeCondition;
} @Override
public void run() {
//注意,这里生产者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
lock.lock();
//先判断有没商品生产,如果没有则开始等到生产者生成
if (Job.value[0] == null) {
try {
//等待生产
System.out.println("我是消费者1当前没有任务,等待生产");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产完成后,唤醒消费者后面的线程任务,消费商品
for (String string : Job.value) {
System.out.println("消费者1清理"+string);
}
System.out.println("消费者1清理完毕,唤醒消费者2进行消费");
//当所有都消费完成后,通知消费者2进行消费
consumeCondition.signal();
//必须要释放锁
lock.unlock(); }
}
新增消费者2:
/**
* 消费者2,等待消费者1清理完成商品后,才开始消费
*/
public class Consume2 implements Runnable {
//获取的锁
Lock lock;
//消费者1和消费者2使用的等待/通知对象,用于绑定消费者1和消费者2的等待/通知关系
Condition consumeCondition; public Consume2(Lock lock, Condition consumeCondition) {
this.lock = lock;
this.consumeCondition = consumeCondition;
} @Override
public void run() {
//注意,这里生产者和消费者必须拿同一把锁,如果锁不相同,则这个机制无法成功使用
lock.lock();
//先判断有没商品生产,如果没有则开始等到生产者生成
if (Job.value[0] == null) {
try {
//等待生产
System.out.println("我是消费者2,当前没有任务,等待生产");
consumeCondition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产完成后,唤醒消费者后面的线程任务,消费商品
for (String string : Job.value) {
System.out.println("消费者2消费"+string);
}
//必须要释放锁
lock.unlock(); }
}
测试类:
public class Job {
//创建一把锁
private static Lock lock = new ReentrantLock();
//生成者和消费者1使用的等待/通知对象,用于绑定生成者和消费者1的等待/通知关系
private static Condition condition = lock.newCondition();
//消费者1和消费者2使用的等待/通知对象,用于绑定消费者1和消费者2的等待/通知关系
private static Condition consumeCondition = lock.newCondition();
public static String value[] = new String[5];
public static void main(String[] args) {
Productor productor = new Productor(lock,condition);
Consume consume = new Consume(lock,condition,consumeCondition);
Consume2 consume2=new Consume2(lock,consumeCondition);
new Thread(consume2).start();
new Thread(consume).start();
new Thread(productor).start();
}
}
运行的结果:

java并发学习--第六章 线程之间的通信的更多相关文章
- java并发学习--第三章 线程安全问题
线程的安全问题一直是我们在开发过程中重要关注的地方,出现线程安全问题的必须满足两个条件:存在着两个或者两个以上的线程:多个线程共享着共同的一个资源, 而且操作资源的代码有多句.接下来我们来根据JDK自 ...
- java并发学习第五章--线程中的锁
一.公平锁与非公平锁 线程所谓的公平,就是指的是线程是否按照锁的申请顺序来获取锁,如果是遵守顺序来获取,这就是个公平锁,反之为非公平锁. 非公平锁的优点在于吞吐量大,但是由于其不是遵循申请锁的顺序来获 ...
- Java并发学习之中的一个——线程的创建
本文是学习网络上的文章时的总结,感谢大家无私的分享. 1.与每一个Java语言中的元素一样,线程是对象.在Java中,我们有两种方式创建线程: a.通过直接继承thread类,然后覆盖run方法. b ...
- java之等待唤醒机制(线程之间的通信)
线程间通信 概念:多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同.比如:线程A用来生成包子的,线程B用来吃包子的,包子可以理解为同一资源,线程A与线程B处理的动作,一个是生产,一个是消 ...
- java并发学习--第四章 JDK提供的线程原子性操作工具类
在了解JDK提供的线程原子性操作工具类之前,我们应该先知道什么是原子性:在多线程并发的条件下,对于变量的操作是线程安全的,不会受到其他线程的干扰.接下来我们就学习JDK中线程的原子性操作. 一.CAS ...
- java并发学习--第七章 JDK提供的线程工具类
一.ThreadLocal ThreadLocal类用于隔离多线程中使用的对象,为ThreadLocal类中传递的泛型就是要隔离的对象,简单的来说:如果我们在主线程创建了一个对象,并且需要给下面的多线 ...
- Java并发学习之十九——线程同步工具之Phaser
本文是学习网络上的文章时的总结.感谢大家无私的分享. JDK 1.7 加入了一个新的工具Phaser.Phaser的在功能上与CountDownLatch有部分重合. 以下使用Phaser类来同步3个 ...
- JAVA多线程学习八-多个线程之间共享数据的方式
多个线程访问共享对象和数据的方式 如果每个线程执行的代码相同,可以使用同一个Runnable对象,这个Runnable对象中有那个共享数据,例如,买票系统就可以这么做. 如果每个线程执行的代码不同,这 ...
- java并发编程(十一)线程间的通信notify通知的遗漏
notify通知的遗漏很容易理解,即threadA还没开始wait的时候,threadB已经notify了,这样,threadB通知是没有任何响应的,当threadB退出synchronized代码块 ...
随机推荐
- 如何降低Vue.js项目中Webpack打包文件的大小?
https://blog.csdn.net/maray/article/details/50988500?utm_source=blogxgwz0 import Blur from ‘vux/src/ ...
- uniapp 之navigateTo:fail page 跳转路径不对
开发uniapp,点击列表跳转详情报错 [system] navigateTo:fail page `/pages/tabBar/index/detail/detail?title=uni-app行业 ...
- JavaSE知识点:finalize,treeMap
*)finalize finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等. *)treeMap 参考 ...
- 北风设计模式课程---接口分离原则(Interface Segregation Principle)
北风设计模式课程---接口分离原则(Interface Segregation Principle) 一.总结 一句话总结: 接口分离原则描述为 "客户类不应被强迫依赖那些它们不需要的接口& ...
- weui 框架
weui是一个框架,但是官方并没有提供文档: 所以我们需要在github上来通过案例来了解他的用法 一般做列表,左边的图片要么60x60,要么80x80用的比较多 weui是一个小巧的框架,所以带来的 ...
- React-Native 之 GD (十三)数据持久化(realm) 及 公共Cell
1.数据持久化 数据持久化是移动端的一个重要部分,刚发现 Realm 原来已经支持 React-Native 了 步骤一: 引入 realm $ npm install realm --save 步骤 ...
- Laravel 在homestead 平台上命令
使用以下命令查看 Heroku 站点地址: $ heroku domains
- P1538迎春舞会之数字舞蹈
传送 输入输出样例:(洛咕的太丑了就不放了) (1前面有三个空格) 这真是一群闲(qian)圈(zou)的人.大号+小号提交了不下10遍终于a了 好了我们来研究一下这些数字"美观" ...
- Java中使用MATLAB作图 .
最近做一个项目,需要很多进行很多信号处理——小魏就是学软件的,对信号处理简直是个小白,最简单的实现就是傻瓜似的调用MATLAB的各种工具箱,达到目的就行. 同时,MATLAB是种解释性语言,执行效率比 ...
- 测开之路一百零一:jquery文字特效、动画、方法链
文字特效 html内容 1.卷起/展开 2.隐藏/显示 3.淡入淡出 <!DOCTYPE html><html lang="en"><head> ...