在上一次【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方法的更多相关文章

  1. 多线程学习-基础(十二)生产者消费者模型:wait(),sleep(),notify()实现

    一.多线程模型一:生产者消费者模型   (1)模型图:(从网上找的图,清晰明了) (2)生产者消费者模型原理说明: 这个模型核心是围绕着一个“仓库”的概念,生产者消费者都是围绕着:“仓库”来进行操作, ...

  2. Java多线程14:生产者/消费者模型

    什么是生产者/消费者模型 一种重要的模型,基于等待/通知机制.生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点: ...

  3. Java多线程-并发协作(生产者消费者模型)

    对于多线程程序来说,不管任何编程语言,生产者和消费者模型都是最经典的.就像学习每一门编程语言一样,Hello World!都是最经典的例子. 实际上,准确说应该是“生产者-消费者-仓储”模型,离开了仓 ...

  4. Linux——多线程下解决生产消费者模型

    我们学习了操作系统,想必对生产消费者问题都不陌生.作为同步互斥问题的一个经典案例,生产消费者模型其实是解决实际问题的基础模型,解决很多的实际问题都会依赖于它.而此模型要解决最大的问题便是同步与互斥.而 ...

  5. 线程高级篇-Lock锁实现生产者-消费者模型

    Lock锁介绍: 在java中可以使用 synchronized 来实现多线程下对象的同步访问,为了获得更加灵活使用场景.高效的性能,java还提供了Lock接口及其实现类ReentrantLock和 ...

  6. 进击的Python【第九章】:paramiko模块、线程与进程、各种线程锁、queue队列、生产者消费者模型

    一.paramiko模块 他是什么东西? paramiko模块是用python语言写的一个模块,遵循SSH2协议,支持以加密和认证的方式,进行远程服务器的连接. 先来个实例: import param ...

  7. 基于线程实现的生产者消费者模型(Object.wait(),Object.notify()方法)

    需求背景 利用线程来模拟生产者和消费者模型 系统建模 这个系统涉及到三个角色,生产者,消费者,任务队列,三个角色之间的关系非常简单,生产者和消费者拥有一个任务队列的引用,生产者负责往队列中放置对象(i ...

  8. 转: 【Java并发编程】之十三:生产者—消费者模型(含代码)

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/17249321 生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一 ...

  9. 【Java并发编程】之十三:生产者—消费者模型

    生产者消费者问题是线程模型中的经典问题:生产者和消费者在同一时间段内共用同一存储空间,生产者向空间里生产数据,而消费者取走数据. ​ 这里实现如下情况的生产--消费模型: ​ 生产者不断交替地生产两组 ...

随机推荐

  1. 学校或公司转ISP -boardband (上网公司)注意事项记录

    如果学校或公司轉boardband , 1. 要更新 domain IP (亦可以轉移domain 去新ISP公司, 要HKDNR 登入名稱和密碼,可問舊ISP即boardband 公司或域名管理方要 ...

  2. 【JavaScript】初识js

    前端三大利器就是HTML+CSS+JavaScript,他们在整个前端开发中的主要作用大体可以概括如下 html 标记语言 负责页面的结构 css 层叠样式表 负责页面的样式 javascript 编 ...

  3. 【VS开发】【C/C++开发】C++参数策略传递内存

    参数策略 如果函数的参数是一个指针,不要指望用该指针去动态申请内存.如下: void GetMemory(char *p, int num) { p = (char *)malloc(sizeof(c ...

  4. 【VS开发】单文档中往视图中加入控件

    [VS开发]单文档中往视图中加入控件 标签(空格分隔): [VS开发] 分隔视图的但文档窗口,要显示控件,推荐使用CFormView或者CCtrlView,前者和对话框的做法一致. 在MainFram ...

  5. Comparator接口实现排序

    对任意类型集合对象进行整体排序,排序时将此接口的实现传递给Collections.sort方法或者Arrays.sort方法排序.实现int compare(T o1, T o2);方法,返回正数,零 ...

  6. [转帖]快速部署Telegraf & Influxdb

    快速部署Telegraf & Influxdb https://www.cnblogs.com/deykenlee/p/7565647.html 作者的blog 比较早 后来 influxdb ...

  7. 有关java5以后的线程

    创建线程的方式 方式一 继承于Thread类 /** * 多线程的创建,方式一:继承于Thread类 * 1. 创建一个继承于Thread类的子类 * 2. 重写Thread类的run() --> ...

  8. 剑指offer7: 斐波那契数列第n项(从0开始,第0项为0)

    1. 题目描述 大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项(从0开始,第0项为0).n<=39 2. 思路和方法 斐波那契数列(Fibonacci sequen ...

  9. Elastic Search中mapping的问题

    Mapping在ES中是非常重要的一个概念.决定了一个index中的field使用什么数据格式存储,使用什么分词器解析,是否有子字段,是否需要copy to其他字段等.Mapping决定了index中 ...

  10. js 中的 number 为何很怪异

    js 中的 number 为何很怪异 声明:需要读者对二进制有一定的了解 对于 JavaScript 开发者来说,或多或少都遇到过 js 在处理数字上的奇怪现象,比如: > 0.1 + 0.2 ...