Java 中 wait, notify 和 notifyAll的正确使用 – 以生产者消费者模型为例
如何使用Wait
尽管关于wait和notify的概念很基础,它们也都是Object类的函数,但用它们来写代码却并不简单。如果你在面试中让应聘者来手写代码,用wait和notify解决生产者消费者问题,我几乎可以肯定他们中的大多数都会无所适从或者犯下一些错误,例如在错误的地方使用 synchronized 关键词,没有对正确的对象使用wait,或者没有遵循规范的代码方法。说实话,这个问题对于不常使用它们的程序员来说确实令人感觉比较头疼。
第一个问题就是,我们怎么在代码里使用wait()呢?因为wait()并不是Thread类下的函数,我们并不能使用Thread.call()。事实上很多Java程序员都喜欢这么写,因为它们习惯了使用Thread.sleep(),所以他们会试图使用wait() 来达成相同的目的,但很快他们就会发现这并不能顺利解决问题。正确的方法是对在多线程间共享的那个Object来使用wait。在生产者消费者问题中,这个共享的Object就是那个缓冲区队列。
第二个问题是,既然我们应该在synchronized的函数或是对象里调用wait,那哪个对象应该被synchronized呢?答案是,那个你希望上锁的对象就应该被synchronized,即那个在多个线程间被共享的对象。在生产者消费者问题中,应该被synchronized的就是那个缓冲区队列。
永远在循环(loop)里调用 wait 和 notify,不是在 If 语句
现在你知道wait应该永远在被synchronized的背景下和那个被多线程共享的对象上调用,下一个一定要记住的问题就是,你应该永远在while循环,而不是if语句中调用wait。因为线程是在某些条件下等待的——在我们的例子里,即“如果缓冲区队列是满的话,那么生产者线程应该等待”,你可能直觉就会写一个if语句。但if语句存在一些微妙的小问题,导致即使条件没被满足,你的线程你也有可能被错误地唤醒。所以如果你不在线程被唤醒后再次使用while循环检查唤醒条件是否被满足,你的程序就有可能会出错——例如在缓冲区为满的时候生产者继续生成数据,或者缓冲区为空的时候消费者开始小号数据。所以记住,永远在while循环而不是if语句中使用wait!
就像我之前说的一样,在while循环里使用wait的目的,是在线程被唤醒的前后都持续检查条件是否被满足。如果条件并未改变,wait被调用之前notify的唤醒通知就来了,那么这个线程并不能保证被唤醒,有可能会导致死锁问题。
Java wait(), notify(), notifyAll() 范例
下面我们提供一个使用wait和notify的范例程序。在这个程序里,我们使用了上文所述的一些代码规范。我们有两个线程,分别名为PRODUCER(生产者)和CONSUMER(消费者),他们分别继承了了Producer和Consumer类,而Producer和Consumer都继承了Thread类。Producer和Consumer想要实现的代码逻辑都在run()函数内。Main线程开始了生产者和消费者线程,并声明了一个LinkedList作为缓冲区队列(在Java中,LinkedList实现了队列的接口)。生产者在无限循环中持续往LinkedList里插入随机整数直到LinkedList满。我们在while(queue.size == maxSize)循环语句中检查这个条件。请注意到我们在做这个检查条件之前已经在队列对象上使用了synchronized关键词,因而其它线程不能在我们检查条件时改变这个队列。如果队列满了,那么PRODUCER线程会在CONSUMER线程消耗掉队列里的任意一个整数,并用notify来通知PRODUCER线程之前持续等待。在我们的例子中,wait和notify都是使用在同一个共享对象上的。
demo: import java.util.LinkedList;
import java.util.Queue;
import java.util.Random; /**
*
* @ClassName: Product
* @Description: 生产者线程类
* @author: xfzhong
* @date: 2017年6月28日
*/
class Product extends Thread{
Queue<Integer> queue;//队列定义,存放数据
int maxSize=0;//容量声明
public Product(Queue<Integer> queue,int maxSize,String name) {//创建线程对象构造函数
super(name);
this.queue=queue;
this.maxSize=maxSize;
}
@Override
public void run() {
while(true){
synchronized(queue){
while(queue.size()==maxSize){
try {
System.out.println("队列已经满了!生产者在等待消费者消耗!");
queue.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
Random random=new Random();
int a=random.nextInt();
System.out.println("Product value:"+a);
queue.add(a);
queue.notifyAll();//队列满了时通知消费者线程开始执行
}
}
}
}
/**
*
* @ClassName: Customer
* @Description: 消费者线程类
* @author: xfzhong
* @date: 2017年6月28日
*/
class Customer extends Thread{
Queue<Integer> queue;//队列定义,存放数据
int maxSize=0;//容量声明
public Customer(Queue<Integer> queue,int maxSize,String name) {//创建线程对象构造函数
super(name);
this.queue=queue;
this.maxSize=maxSize;
}
@Override
public void run() {
while(true){
synchronized (queue) {
while(queue.isEmpty()){
try {
System.out.println("队列为空!消费者在等待生产者生产!");
queue.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
System.out.println("customer value:"+queue.remove());
queue.notifyAll();
}
}
}
}
public class ProductOrCustomer {
public static void main(String[] args) {
//创建线程对象
System.out.println("start-----");
Queue<Integer> queue=new LinkedList<Integer>();
int maxSize=10;
Product product=new Product(queue, maxSize, "product");
Customer customer=new Customer(queue, maxSize, "customer");
product.start();
customer.start();
System.out.println("end-----");
}
}
Java 中 wait, notify 和 notifyAll的正确使用 – 以生产者消费者模型为例的更多相关文章
- 如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...
- java中的notify和notifyAll有什么区别?
先说两个概念:锁池和等待池 锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized块),由于这些线程在进入 ...
- 生产者-消费者模型在Hudi中的应用
介绍 生产者-消费者模型用于解耦生产者与消费者,平衡两者之间的能力不平衡,该模型广泛应用于各个系统中,Hudi也使用了该模型控制对记录的处理,即记录会被生产者生产至队列中,然后由消费者从队列中消费,更 ...
- Java Thread wait, notify and notifyAll Example
Java Thread wait, notify and notifyAll Example Java线程中的使用的wait,notify和nitifyAll方法示例. The Object clas ...
- [译]Java Thread wait, notify和notifyAll示例
Java Thread wait, notify和notifyAll示例 Java上的Object类定义了三个final方法用于不同线程间关于某资源上的锁状态交互,这三个方法是:wait(), not ...
- 多线程-4.wait() notify() notifyAll() 生产者消费者模型
1.wait()方法 该方法继承于Object类.在调用obj.wait()方法后,当前线程会失去obj的锁.待其他线程调用obj.notify()或notifyAll()方法后进入锁等待池,争抢到锁 ...
- 生产者消费者模型——wait/notify/notifyAll使用
告警系统架构如下 1. 数据处理系统处理完原始数据并入库后,发送消息到kafka系统: 2. 告警生产者从kafka系统查询消息存入告警消息队列: 3. 告警消费者从告警消息队列查询消息进行处理. 这 ...
- java线程基础巩固---多线程下的生产者消费者模型,以及详细介绍notifyAll方法
在上一次[http://www.cnblogs.com/webor2006/p/8419565.html]中演示了多Product多Consumer假死的情况,这次解决假死的情况来实现一个真正的多线程 ...
- 第23章 java线程通信——生产者/消费者模型案例
第23章 java线程通信--生产者/消费者模型案例 1.案例: package com.rocco; /** * 生产者消费者问题,涉及到几个类 * 第一,这个问题本身就是一个类,即主类 * 第二, ...
随机推荐
- c# DataTable行转列
/// <summary> /// datatable行转列 /// </summary> /// <param name="dtSrc">来源 ...
- (转)MapReduce Design Patterns(chapter 7 (part 1))(十三)
CHAPTER 7.Input and Output Patterns 本章关注一个最经常忽略的问题,来改进MapReduce 的value:自定义输入和输出.我们并不会总使用Mapreduce本身的 ...
- 每周荐书:Kotlin、分布式、Keras(评论送书)
每周荐书:Kotlin.分布式.Keras(评论送书) 感谢大家对每周荐书栏目的支持,先公布下上周中奖名单 法式三文鱼 名优秀评论可以免费获得此书. Kotlin实战 首著席卷而来 Android ...
- NamedParameterJdbcTemplate常用方法总结
数据库结构 1.插入/修改/删除数据,使用update方法 插入数据1 API: int update(String sql, Map< String, ? > paramMap) 示例: ...
- I.MX6 I2C DS1337 disable square-wave output
linux I2C DS1337 disable square-wave output \\\\\\\\\\\-*- 目录 -*-////////// | 一.DS1337访问寄存器说明: | 二.c ...
- 兼容iOs7的自定义alertView
转载请注明出处. 升级到ios7后,旧项目中使用的继承UIAlertView的自定义alertview无法正常显示了,无奈只好换思路去实现,改成从当前keywindow下创建要显示的alertview ...
- ACM 五一杭电赛码"BestCoder"杯中国大学生程序设计冠军赛小记
对于这项曾经热爱的竞赛,不得不说这是我最后一年参加ACM比赛了,所以要珍惜每一次比赛的机会. 五一去杭电参加了赛码"BestCoder"杯中国大学生程序设计冠军赛,去的队伍包括了今 ...
- 《selenium2 python 自动化测试实战》(4)——鼠标事件
鼠标事件包含在ActionChains类中,导入时只需要: from selenium.webdriver.common.action_chains import ActionChains 导入类即可 ...
- [BZOJ2115][WC2011]最大XOR和路径
bzoj luogu sol 首先很显然的,答案等于1到n的任意一条路径的异或和与若干个环的异或和的异或和. 因为图是联通的,那么就可以从一个点走到任意一个想要走到的环上,走完这个环后原路返回,那么中 ...
- Java变量类型之间的转换
int i; String s="134"; 1)String转换成int i=Integer.parseInt(s); 2)int转换成String(其他类型转String,都可 ...