在上一次【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. bzoj 4736: 温暖会指引我们前行 (LCT 维护最大生成树)

    链接:https://www.lydsy.com/JudgeOnline/problem.php?id=4736 题面: 寒冬又一次肆虐了北国大地 无情的北风穿透了人们御寒的衣物 可怜虫们在冬夜中发出 ...

  2. (5.15)mysql高可用系列——MHA实践

    关键词:MHA,mysql mha [1]需求 采用mysql技术,实现MHA高可用主从环境,预计未来数据量几百G MHA概念参考:MYSQL高可用技术概述 [2]环境技术架构 [2.1]MHA简介 ...

  3. OracleLinux6安装

    针对Oracle数据库安装的linux系统 1.首先要有oracle linux的镜像 链接:https://pan.baidu.com/s/1S3xYr4YNGtU-351bVaS1-Q 提取码:a ...

  4. 阿里云云计算ACP专业认证考试

    阿里云云计算专业认证(Alibaba Cloud Certified Professional,ACP)是面向使用阿里云云计算产品的架构.开发.运维类人员的专业技术认证. 更多阿里云云计算ACP专业认 ...

  5. DP大大大大大赏

    还是前置: 动态规划的三种实现方法: 递推,递归,记忆化搜索 然后还是从斐波那契数列开始引入: 两种求斐波那契数列的方法: 1.用其他位置的结果得到自己的结果: 2.用自己的结果算其他的结果: 以上两 ...

  6. CSS:盒子的定位与浮动

    CSS--盒子定位.浮动与居中 HTML中的每个元素都是一个盒子   浏览器对HTML文档进行解析,根据盒子的属性对其进行排列. 每个元素默认使用标准文档流定位   标准文档流:是指浏览器读取HTML ...

  7. hard or 9102 字符串DP---Educational Codeforces Round 57 (Rated for Div. 2)

    题意:http://codeforces.com/problemset/problem/1096/D 思路:参考:https://blog.csdn.net/qq_41289920/article/d ...

  8. Python 基础(十七)--序列化

    pickle模快 pickle.dumps():可将特有数据类型的转换为bytes类型 pickle.loads():将bytes类型转化回来 >>> import pickle & ...

  9. 代码优化:Java编码技巧之高效代码50例

    出处:  Java编码技巧之高效代码50例 1.常量&变量 1.1.直接赋值常量值,禁止声明新对象 直接赋值常量值,只是创建了一个对象引用,而这个对象引用指向常量值. 反例: Long i = ...

  10. python基础知识0-3

    一.根据用户输入内容输出其权限 # 根据用户输入内容打印其权限    # FYT --> 超级管理员 # eric --> 普通管理员 # tony,rain --> 业务主管 # ...