1. 单线程下的生产者--消费者模式

1.1 该模式下,一个线程生产数据,另一个线程处理数据。当数据还没被处理,那么生产数据的线程进入等待状态;如果数据还没生产,那么处理数据的线程进入等待状态,代码及运行结果如下。

// 生产者
class Producer extends Thread{private final List<Object> lst;public Producer(List<Object> lst){this.lst = lst;}@Overridepublic void run() {while(true){synchronized (lst){if(lst.size() > 0){try {lst.wait();} catch (InterruptedException e) {e.printStackTrace();}}lst.add(new Object());System.out.println("生产者:" + lst.size());lst.notify();}}}
}// 消费者
class Consumer extends Thread{private final List<Object> lst;public Consumer(List<Object> lst){this.lst = lst;}@Overridepublic void run() {while(true){synchronized (lst){if(lst.size() == 0){try {lst.wait();} catch (InterruptedException e) {e.printStackTrace();}}lst.remove(0);System.out.println("消费者:" + lst.size());lst.notify();}}}
}

1.2 功能分析。生产者和消费者共享同一个ArrayList对象,生产者往ArrayList放数据,消费者删除数据。初始时,ArrayList里没有数据,如果消费者先操作ArrayList对象,判断ArrayList对象没有数据后,执行wait方法进入等待状态并释放ArrayList对象锁;此时生产者得到该锁,判断ArrayList没有数据后,往里面添加一个数据,并调用notify方法唤醒进入等待状态的消费者。如果此时生产者又抢到ArrayList对象锁,那么判断ArrayList对象有数据后,会进入等待状态,前面被唤醒的消费者来消费并唤醒刚进入等待的生产者,后面一直如此循环....

2. wait和notify方法

2.1 wait方法,执行该方法的线程会进入阻塞状态,而不是可运行状态,所以不会去抢夺CPU时间片;同时会释放正在占用的对象锁。

2.2 关于执行wait方法的线程是否会抢夺CPU时间片,下面通过资源监视器来证明一下。当我没有运行java程序时,CPU的利用率是6%左右,如下图。

当java创建10个线程,一个while循环,没有任何逻辑代码,CPU利用率达到了100%。

此时我们在while循环中写上wait方法,我们可以发现,CPU利用率只有刚创建线程时会有短暂的升高,后面线程执行了wait方法进入等待状态并没有导致CPU利用率升高,所以执行wait方法的线程不会抢夺CPU时间片。

同理,sleep方法也并不会抢夺CPU时间片,但sleep方法不释放锁。

2.3 wait和sleep的区别

① wait是Object的方法,sleep是Thread的方法。

② wait会释放占有的锁,但sleep不会释放锁。

③ wait必须用在同步代码块或同步方法中,sleep不需要。

④ 它们都不会去抢夺CPU时间片

⑤ 调用wait方法时线程会进入waiting状态;而调用sleep方法时线程会进入timed_waiting状态;线程等待synchronized同步代码块或同步方法时进入blocked状态。

2.4 notify方法:唤醒等待状态的线程,将线程从阻塞状态变为可运行状态,抢到CPU时间片再执行;同时还要占有锁才能执行。

3. 多线程下的生产者消费者模式

3.1 我们先使用第一节的消费者和生产者代码,分别创建两个消费者和两个生产者,得到运行结果如下。

3.2 出现一直生产的情况的原因:生产了数据的生产者会唤醒一个正在等待状态的线程,如果此时唤醒的是另一个生产者,那么又会生产数据,然后该生产者会有唤醒一个等待状态的线程,如果此时唤醒的又是生产者,那么循环往复,就会一直生产数据....

3.3 既然出现一直生产的情况,是因为被唤醒直接生产数据,那么就在被唤醒后仍然判断一下是否有数据,如果有,再进入等待状态,代码如下。

3.4 运行后,会出现卡死情况,原因:当没有数据时,两个消费者会进入等待状态,此时如果一个生产者占有锁,它会生产一个数据,如果此时唤醒的是一个等待状态的生产者,那么生产者还是进入等待,等到自己再次抢到锁,依然因为有数据而进入等待状态,也就出现了卡死现象。

3.5 既然是因为都进入了等待状态,那么每次都用notifyAll方法,把所有等待状态的线程都唤醒,就不会出现卡死现象了,最终代码如下。

import java.util.ArrayList;
import java.util.List;public class WaitNotifyTest {public static void main(String[] args) {List<Object> lst = new ArrayList<>();Producer pro = new Producer(lst);Consumer con = new Consumer(lst);pro.start();con.start();new Producer(lst).start();new Consumer(lst).start();}
}class Producer extends Thread{private final List<Object> lst;public Producer(List<Object> lst){this.lst = lst;}@Overridepublic void run() {while(true){synchronized (lst){while(lst.size() > 0){try {lst.wait();} catch (InterruptedException e) {e.printStackTrace();}}lst.add(new Object());System.out.println("生产者:" + lst.size());lst.notifyAll();}}}
}class Consumer extends Thread{private final List<Object> lst;public Consumer(List<Object> lst){this.lst = lst;}@Overridepublic void run() {while(true){synchronized (lst){while(lst.size() == 0){try {lst.wait();} catch (InterruptedException e) {e.printStackTrace();}}lst.remove(0);System.out.println("消费者:" + lst.size());lst.notifyAll();}}}
}

4. 通过钩子程序捕获程序退出以及setUncaughtExceptionHandler方法捕获线程抛出的运行时异常

单线程下的生产者--消费者模式详解,wait和sleep的区别相关推荐

  1. 生产者消费者模式详解(转载)

    ★简介 在实际的软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类.函数.线程.进程等).产生数据的模块,就形象地称为生产者:而处理 ...

  2. Java多线程(含生产者消费者模式详解)

    多线程 导航 多线程 1 线程.进程.多线程概述 2 创建线程 (重点) 2.1 继承Thread类(Thread类也实现了Runnable接口) 2.2 实现Runnable接口(无消息返回) 2. ...

  3. java中的生产者消费者模式详解

    方式 一: Synchronized方式 注:此种方式会造成资源的浪费: 利用锁的notifyAll()方法会将所有的线程都唤醒,会造成资源的浪费 class Resource {private St ...

  4. 生产者消费者模型详解以及实现

    生产者消费者模式 我们先来看看什么是生产者消费者模式,生产者消费者模式是程序设计中非常常见的一种设计模式,被广泛运用在解耦.消息队列等场景.在现实世界中,我们把生产商品的一方称为生产者,把消费商品的一 ...

  5. 操作系统实验 生产者消费者问题详解

    操作系统课程设计 生产者消费者实验报告 一.实验目的 加深对进程概念的理解,明确进程与程序的区别. 认识并发执行的本质. 理解和掌握Linux和Windows进程通信系统调用的功能,通过实验和学习,提 ...

  6. 生产者消费者模型详解

    生产者消费者模型 文章目录 生产者消费者模型 什么是生产者消费者模型 基于BlockingQueue的生产者消费者模型 单生产者单消费者模型 多生产者多消费者模型 什么是生产者消费者模型 生产者消费者 ...

  7. 生产者消费者模型---详解及代码实现

    概念 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者 ...

  8. C++11 并发指南九(综合运用: C++11 多线程下生产者消费者模型详解)

    前面八章介绍了 C++11 并发编程的基础(抱歉哈,第五章-第八章还在草稿中),本文将综合运用 C++11 中的新的基础设施(主要是多线程.锁.条件变量)来阐述一个经典问题--生产者消费者模型,并给出 ...

  9. Java线程生产者消费者问题详解

    问题描述         生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例.生产 ...

最新文章

  1. :link,:visited,:focus,:hover,:active详解
  2. linux7 rpmdb 修复,Linux[CentOS 7]rpmdb open failed错误修复
  3. RabbitMQ(一):Hello World程序
  4. 算法笔记--八个常见排序算法总结
  5. ModChip and more
  6. :empty css 可以用在哪些标签,CSS3 :empty 选择器
  7. nginx 访问图片404_nginx发布vue 项目
  8. java导出sas_[转载]SAS Proc Export导出文件
  9. 使用ipmitool命令检测电源模块状态
  10. SAP的十年豪赌:不成HANA 便成仁
  11. python连接pymysql主机目标无响应_Python 解析pymysql模块操作数据库的方法
  12. license.xml
  13. 常用通信光纤是如何分类的
  14. js 字符串 常用方法
  15. css聊天气派,css如何实现小尖角聊天对话框带尖角的说话泡泡效果
  16. 杰理之BQB 的 RF 测试【篇】
  17. 想要申请免费的云主机可以怎么做
  18. linux 共享文件夹 安装,linux – Vagrant并在共享文件夹中安装包
  19. 解决vue 不支持ie浏览器 qq浏览器的解决办法
  20. 被腾讯起诉抄袭《王者荣耀》的公司发声明,重点却是法官

热门文章

  1. ABAP如何检查字符串是否为日期或时间格式
  2. 【转】SAP开关账期后台任务
  3. SAP内存和ABAP内存的简单介绍说明
  4. 项目中遇到不善于表达的人,该如何沟通?
  5. 销售订单行项目的装运点字段确认规则
  6. 工作总结的写作方法与要领
  7. MRP清单的动态和静态ABAP的取法
  8. 冬季会增加某些疾病的发病风险吗?
  9. python的主要应用领域及常用的函数模块_python之常用模块篇5
  10. 常用来进行钢结构节点输出的软件是什么_纯干货:钢结构设计入门知识及简易方法(建议收藏)...