java线程基础巩固---多线程下的生产者消费者模型,以及详细介绍notifyAll方法
在上一次【http://www.cnblogs.com/webor2006/p/8419565.html】中演示了多Product多Consumer假死的情况,这次解决假死的情况来实现一个真正的多线程下的生产者消费者模型,在解决之前来看一下wait()和notify()的官方文档,因为假死的原因就跟这两方法有关:

而其中0就代表永远等待,如果给wait中传一个大于0的参数那就是wait指定时间之后就不wait了,好继续往下看wait()的官方注释:

其这句话说到了一个重点:调用wait()方法其实是释放了监听器的所有权,并且当被唤醒之后并非立马就能够执行,而是需要再去获取直接获取成功之后才会执行它下面的代码。而对于wait()过的线程是需要能过notify()或notifyAll()来唤醒的,而notify()是通知一个线程唤醒,而notifyAll()是会将wait()在同一个monitor的所有线程都会唤醒,而解决之前多个生产者与消费者死锁就得用到notifyAll()这个方法了,下面来用它将死锁的程序进行改造,再改之前先贴一下原来有BUG的代码:
public class ProductConsumerVersion2 {
private final Object LOCK = new Object();
private int i = 1;
/* 此标识用来说明是否当前已经生产过了,默认没有 */
private volatile boolean isProduced = false;
private void product() {
synchronized (LOCK) {
if (isProduced) {
try {
System.out.println(Thread.currentThread().getName() + " wait了");
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
i++;
System.out.println(Thread.currentThread().getName() + " 生产了-->" + i);
LOCK.notify();
System.out.println(Thread.currentThread().getName() + " notify了");
isProduced = true;
}
}
}
private void comsume() {
synchronized (LOCK) {
if (isProduced) {
System.out.println(Thread.currentThread().getName() + " 消费了-->" + i);
LOCK.notify();
System.out.println(Thread.currentThread().getName() + " notify了");
isProduced = false;
} else {
try {
System.out.println(Thread.currentThread().getName() + " wait了");
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
ProductConsumerVersion2 productConsumerVersion2 = new ProductConsumerVersion2();
Stream.of("P1", "P2").forEach(n -> new Thread(n) {
@Override
public void run() {
while (true)
productConsumerVersion2.product();
}
}.start());
Stream.of("C1", "C2").forEach(n -> new Thread(n) {
@Override
public void run() {
while (true)
productConsumerVersion2.comsume();
}
}.start());
}
}
好,接下来进行改造:

类似的,对于消费方法也进行相应的修改:

那为什么要用while进行改造呢?如果改用if就不行么,那假设改用if也可以,那咱们来分析下:

如果有两个生产者线程p1、p2,一个消费者线程c1,目前队列中已经有一个数据待c1进行消费,所以此时p1、p2都已经wait()住了,因为得待c1消费完来notifyall();这时c1将队列中的数据消费掉了然后用notifyAll()通知p1、p2进行数据生产,此时p1抢到锁了,于是乎会往下执行数据生产,如下:

而当执行完p1就释放锁了,此时正在wait()的p2抢到锁之后由于是if,所以也开始生产数据了,这样就出现一个尴尬的局面:生产了两个数据,然后才开始消费,而咱们预期的是生产一个消费一个,所以这就是为啥需要用while的原因所在,下面用程序来演示一下这种异常情况:
public class ProductConsumerVersion3 {
private final Object LOCK = new Object();
private int i = 1;
/* 此标识用来说明是否当前已经生产过了,默认没有 */
private volatile boolean isProduced = false;
private void product() {
synchronized (LOCK) {
if (isProduced) {
try {
// System.out.println(Thread.currentThread().getName() + " wait了");
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
i++;
System.out.println(Thread.currentThread().getName() + " 生产了-->" + i);
LOCK.notifyAll();
// System.out.println(Thread.currentThread().getName() + " notify了");
isProduced = true;
}
}
private void comsume() {
synchronized (LOCK) {
if (!isProduced) {
try {
// System.out.println(Thread.currentThread().getName() + " wait了");
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 消费了-->" + i);
LOCK.notifyAll();
// System.out.println(Thread.currentThread().getName() + " notify了");
isProduced = false;
}
}
public static void main(String[] args) {
ProductConsumerVersion3 productConsumerVersion2 = new ProductConsumerVersion3();
//用三个生产者和二个消费者来模式,貌似生产者与消费者的个数一样难得出我们预期的异常情况
Stream.of("P1", "P2", "P3").forEach(n -> new Thread(n) {
@Override
public void run() {
while (true) {
productConsumerVersion2.product();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start());
Stream.of("C1", "C2").forEach(n -> new Thread(n) {
@Override
public void run() {
while (true) {
productConsumerVersion2.comsume();
try {//为了便于观察打印这里小休眠一会
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start());
}
}
编译运行:

解决办法也就是将其if改为while啦,如下:
public class ProductConsumerVersion3 {
private final Object LOCK = new Object();
private int i = 1;
/* 此标识用来说明是否当前已经生产过了,默认没有 */
private volatile boolean isProduced = false;
private void product() {
synchronized (LOCK) {
while (isProduced) {
try {
// System.out.println(Thread.currentThread().getName() + " wait了");
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
i++;
System.out.println(Thread.currentThread().getName() + " 生产了-->" + i);
LOCK.notifyAll();
// System.out.println(Thread.currentThread().getName() + " notify了");
isProduced = true;
}
}
private void comsume() {
synchronized (LOCK) {
while (!isProduced) {
try {
// System.out.println(Thread.currentThread().getName() + " wait了");
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " 消费了-->" + i);
LOCK.notifyAll();
// System.out.println(Thread.currentThread().getName() + " notify了");
isProduced = false;
}
}
public static void main(String[] args) {
ProductConsumerVersion3 productConsumerVersion2 = new ProductConsumerVersion3();
Stream.of("P1", "P2", "P3").forEach(n -> new Thread(n) {
@Override
public void run() {
while (true) {
productConsumerVersion2.product();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start());
Stream.of("C1", "C2").forEach(n -> new Thread(n) {
@Override
public void run() {
while (true) {
productConsumerVersion2.comsume();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start());
}
}
编译运行:

所以下面对其总结一下:
1、为啥木有死锁了,是因为将notify改为notifyAll了。
2、为啥生产消费木有错乱,是因为使用了while循环来判断是不需要进行wait()。
java线程基础巩固---多线程下的生产者消费者模型,以及详细介绍notifyAll方法的更多相关文章
- 多线程学习-基础(十二)生产者消费者模型:wait(),sleep(),notify()实现
一.多线程模型一:生产者消费者模型 (1)模型图:(从网上找的图,清晰明了) (2)生产者消费者模型原理说明: 这个模型核心是围绕着一个“仓库”的概念,生产者消费者都是围绕着:“仓库”来进行操作, ...
- Java多线程14:生产者/消费者模型
什么是生产者/消费者模型 一种重要的模型,基于等待/通知机制.生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点: ...
- Java多线程-并发协作(生产者消费者模型)
对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一样,Hello World!都是最经典的例子. 实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓 ...
- Linux——多线程下解决生产消费者模型
我们学习了操作系统,想必对生产消费者问题都不陌生.作为同步互斥问题的一个经典案例,生产消费者模型其实是解决实际问题的基础模型,解决很多的实际问题都会依赖于它.而此模型要解决最大的问题便是同步与互斥.而 ...
- 线程高级篇-Lock锁实现生产者-消费者模型
Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...
- 进击的Python【第九章】:paramiko模块、线程与进程、各种线程锁、queue队列、生产者消费者模型
一.paramiko模块 他是什么东西? paramiko模块是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接. 先来个实例: import param ...
- 基于线程实现的生产者消费者模型(Object.wait(),Object.notify()方法)
需求背景 利用线程来模拟生产者和消费者模型 系统建模 这个系统涉及到三个角色,生产者,消费者,任务队列,三个角色之间的关系非常简单,生产者和消费者拥有一个任务队列的引用,生产者负责往队列中放置对象(i ...
- 转: 【Java并发编程】之十三:生产者—消费者模型(含代码)
转载请注明出处:http://blog.csdn.net/ns_code/article/details/17249321 生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一 ...
- 【Java并发编程】之十三:生产者—消费者模型
生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据. 这里实现如下情况的生产--消费模型: 生产者不断交替地生产两组 ...
随机推荐
- 【无线安全实践入门】破解WiFi密码的多个方法
本文希望可以帮助到想要学习接触此方面.或兴趣使然的你,让你有个大概的印象. 文中可能存在错误操作或错误理解,望大家不吝指正. !阅前须知! 本文是基于我几年前的一本笔记本,上面记录了我学习网络基础时的 ...
- PHP SQL注入
开发者容易遗漏的输入点: HTTP头 X-Forwarded-For 获取用户ip User-Agent 获取浏览器 Referer 获取之 ...
- zping ping包工具20180605.exe测试版
链接: https://pan.baidu.com/s/1WB3BZn0r9n4DRU_8bNC65g 提取码: mybi zping的第一个exe版本由于未对兼容性进行测试,使用python3.6编 ...
- 【转帖】CentOS 7 修改时区
CentOS 7 修改时区 https://www.cnblogs.com/yaohong/p/7269878.html timedatectl 以及 time 分类: 操作系统-CentOS u ...
- PAT B1046.猜拳
课本AC #include <cstdio> int main() { int n, failA = 0, failB = 0; scanf("%d", &n) ...
- spark on yarn UI界面详解
参考: spark on yarn图形化任务监控利器:History-server帮你理解spark的任务执行过程 spark内存分配原理 yarn运行原理详解 task,executor,core等 ...
- [经验分享] Docker网络解决方案-Weave部署记录
前面说到了Flannel的部署,今天这里说下Docker跨主机容器间网络通信的另一个工具Weave的使用.当容器分布在多个不同的主机上时,这些容器之间的相互通信变得复杂起来.容器在不同主机之间都使用的 ...
- C# 不用添加WebService引用,调用WebService方法
// 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消注释以下行. [System.Web.Script.Services.ScriptService] 使用HttpWeb ...
- Pytorch中nn.Conv2d的用法
Pytorch中nn.Conv2d的用法 nn.Conv2d是二维卷积方法,相对应的还有一维卷积方法nn.Conv1d,常用于文本数据的处理,而nn.Conv2d一般用于二维图像. 先看一下接口定义: ...
- JavaScript函数尾调用与尾递归
什么是函数尾调用和尾递归 函数尾调用与尾递归的应用 一.什么是函数的尾调用和尾递归 函数尾调用就是指函数的最后一步是调用另一个函数. //函数尾调用示例一 function foo(x){ retur ...