当前位置: 首页 > news >正文

重学java 43.多线程 多等待多唤醒案例

Fear never builds the future,but hope does.

                                                          —— 24.5.25

多等待多唤醒问题

        在多条线程同时消费同时等待时,会出现问题

BaoZiPu

package S77ThreadMoreWait;/*count和flag可以定义成包装类,但要记得给count和flag手动赋值不然对于本案例来说,容易出现空指针异常*/
public class BaoZiPu {// 包子的数目countprivate int count;// 是否有包子flagprivate boolean flag;public BaoZiPu() {}public BaoZiPu(int count, boolean flag) {this.count = count;this.flag = flag;}// getCount改成消费包子,直接输出包子数量countpublic void getCount() {System.out.println("消费了第"+count+"个包子");}// setCount改造成生产包子,count++public void setCount() {count++;System.out.println("生产了第"+count+"个包子");}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}
}

生产product线程

package S77ThreadMoreWait;// 实现Runnable接口
public class Product implements Runnable{BaoZiPu baoZiPu = new BaoZiPu();// 提供一个有参构造public Product(BaoZiPu baoZiPu){this.baoZiPu = baoZiPu;}@Overridepublic void run() {// 定义一个死循环while(true) {try {Thread.sleep(100L);}catch (InterruptedException e){throw new RuntimeException(e);}// 同步代码块synchronized (baoZiPu){// 1.判断flag是否为true,如果是true,证明有包子,生产线程等待if(baoZiPu.isFlag()==true){try{baoZiPu.wait();}catch(InterruptedException e){throw new RuntimeException(e);}}// 2.如果flag为false,证明没有包子,则要开始生产baoZiPu.setCount();// 3.改变flag为truebaoZiPu.setFlag(true);// 4.唤醒所有等待线程baoZiPu.notify();}}}
}

消费consumer线程

package S77ThreadMoreWait;public class Consumer implements Runnable{BaoZiPu baoZiPu = new BaoZiPu();// 提供一个有参构造public Consumer(BaoZiPu baoZiPu){this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true) {try {Thread.sleep(100L);}catch (InterruptedException e){throw new RuntimeException(e);}// 同步代码块synchronized (baoZiPu){// 1.判断flag是否为false,如果是false,证明没有包子,消费线程等待if(baoZiPu.isFlag()==false){// 抛出异常try{baoZiPu.wait();}catch(InterruptedException e){throw new RuntimeException(e);}}// 2.如果flag为true,则要开始消费baoZiPu.getCount();// 3.改变flag为false,消费完了,没有包子了baoZiPu.setFlag(false);// 4.唤醒所有等待线程baoZiPu.notify();}}}
}

测试类

package S77ThreadMoreWait;public class Demo216Test {public static void main(String[] args) {// 变成同一个对象BaoZiPu baoZiPu = new BaoZiPu();// 把baozipu对象分别传给两个线程中Product product = new Product(baoZiPu);Consumer consumer = new Consumer(baoZiPu);// 三条生产线程new Thread(product).start();new Thread(product).start();new Thread(product).start();// 三条消费线程new Thread(consumer).start();new Thread(consumer).start();new Thread(consumer).start();}
}

运行结果:会出现连续生产和连续消费的行为

         多条线程在同时等待时,上一条线程结束之后会随机唤醒一条线程,所以不能确定具体的顺序 

解决尝试 — 唤醒全部线程 notifyAll

   生产线程Product

package S77ThreadMoreWait;// 实现Runnable接口
public class Product implements Runnable{BaoZiPu baoZiPu = new BaoZiPu();// 提供一个有参构造public Product(BaoZiPu baoZiPu){this.baoZiPu = baoZiPu;}@Overridepublic void run() {// 定义一个死循环while(true) {try {Thread.sleep(100L);}catch (InterruptedException e){throw new RuntimeException(e);}// 同步代码块synchronized (baoZiPu){// 1.判断flag是否为true,如果是true,证明有包子,生产线程等待if(baoZiPu.isFlag()==true){try{baoZiPu.wait();}catch(InterruptedException e){throw new RuntimeException(e);}}// 2.如果flag为false,证明没有包子,则要开始生产baoZiPu.setCount();// 3.改变flag为truebaoZiPu.setFlag(true);// 4.唤醒所有等待线程baoZiPu.notifyAll();}}}
}

   消费线程Consumer

package S77ThreadMoreWait;public class Consumer implements Runnable{BaoZiPu baoZiPu = new BaoZiPu();// 提供一个有参构造public Consumer(BaoZiPu baoZiPu){this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true) {try {Thread.sleep(100L);}catch (InterruptedException e){throw new RuntimeException(e);}// 同步代码块synchronized (baoZiPu){// 1.判断flag是否为false,如果是false,证明没有包子,消费线程等待if(baoZiPu.isFlag()==false){// 抛出异常try{baoZiPu.wait();}catch(InterruptedException e){throw new RuntimeException(e);}}// 2.如果flag为true,则要开始消费baoZiPu.getCount();// 3.改变flag为false,消费完了,没有包子了baoZiPu.setFlag(false);// 4.唤醒所有等待线程baoZiPu.notifyAll();}}}
}

  运行结果:会出现连续生产和连续消费的行为

解决尝试 — 把 if 改成 while

 生产线程Product

package S77ThreadMoreWait;// 实现Runnable接口
public class Product implements Runnable{BaoZiPu baoZiPu = new BaoZiPu();// 提供一个有参构造public Product(BaoZiPu baoZiPu){this.baoZiPu = baoZiPu;}@Overridepublic void run() {// 定义一个死循环while(true) {try {Thread.sleep(100L);}catch (InterruptedException e){throw new RuntimeException(e);}// 同步代码块synchronized (baoZiPu){// 1.判断flag是否为true,如果是true,证明有包子,生产线程等待while (baoZiPu.isFlag()==true){try{baoZiPu.wait();}catch(InterruptedException e){throw new RuntimeException(e);}}// 2.如果flag为false,证明没有包子,则要开始生产baoZiPu.setCount();// 3.改变flag为truebaoZiPu.setFlag(true);// 4.唤醒所有等待线程baoZiPu.notifyAll();}}}
}

 消费线程Consumer

package S77ThreadMoreWait;public class Consumer implements Runnable{BaoZiPu baoZiPu = new BaoZiPu();// 提供一个有参构造public Consumer(BaoZiPu baoZiPu){this.baoZiPu = baoZiPu;}@Overridepublic void run() {while(true) {try {Thread.sleep(100L);}catch (InterruptedException e){throw new RuntimeException(e);}// 同步代码块synchronized (baoZiPu){// 1.判断flag是否为false,如果是false,证明没有包子,消费线程等待while (baoZiPu.isFlag()==false){// 抛出异常try{baoZiPu.wait();}catch(InterruptedException e){throw new RuntimeException(e);}}// 2.如果flag为true,则要开始消费baoZiPu.getCount();// 3.改变flag为false,消费完了,没有包子了baoZiPu.setFlag(false);// 4.唤醒所有等待线程baoZiPu.notifyAll();}}}
}

 运行结果:不会出现连续生产和连续消费的行为

总结:notifyAll和while要一起执行,才可以保证我们的代码不会出现错误情况

相关文章:

  • 智能家居完结 -- 整体设计
  • 汽车以太网发展现状及挑战
  • 前台常见功能解决方案:下载+全屏+引导
  • kali基本扫描工具(自带)
  • XSS 攻击
  • Codeforces Round 927 (Div. 3) D. Card Game 题解 贪心
  • 基于Hadoop技术的智慧图书馆海量数据储存系统研究
  • Debezium+Kafka:Oracle 11g 数据实时同步至 DolphinDB 解决方案
  • 【C++课程学习】:命名空间的理解(图文详解)
  • i2c总线介绍
  • 文心智能体大赛:百度文心智能体平台初体验
  • 基于webpack+Vue3+JavaScript+antd+less+axios技术栈实现所有组件全局自动化注册
  • [JDK工具-5] jinfo jvm配置信息工具
  • 自从有了可观测性,传统运维如何进行提升?
  • Flutter 中的 ClipRect 小部件:全面指南
  • Computed property XXX was assigned to but it has no setter
  •  D - 粉碎叛乱F - 其他起义
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • HomeBrew常规使用教程
  • Joomla 2.x, 3.x useful code cheatsheet
  • PAT A1017 优先队列
  • Python语法速览与机器学习开发环境搭建
  • springMvc学习笔记(2)
  • SSH 免密登录
  • Vue2.0 实现互斥
  • WebSocket使用
  • weex踩坑之旅第一弹 ~ 搭建具有入口文件的weex脚手架
  • 成为一名优秀的Developer的书单
  • 基于web的全景—— Pannellum小试
  • 前嗅ForeSpider采集配置界面介绍
  • 如何设计一个微型分布式架构?
  • 数组大概知多少
  • 小程序测试方案初探
  • 学习笔记:对象,原型和继承(1)
  • 原生 js 实现移动端 Touch 滑动反弹
  • 找一份好的前端工作,起点很重要
  • 策略 : 一文教你成为人工智能(AI)领域专家
  • 从如何停掉 Promise 链说起
  • # Swust 12th acm 邀请赛# [ E ] 01 String [题解]
  • #我与Java虚拟机的故事#连载06:收获颇多的经典之作
  • #我与Java虚拟机的故事#连载10: 如何在阿里、腾讯、百度、及字节跳动等公司面试中脱颖而出...
  • (2022 CVPR) Unbiased Teacher v2
  • (3)STL算法之搜索
  • (libusb) usb口自动刷新
  • (ZT)薛涌:谈贫说富
  • (编程语言界的丐帮 C#).NET MD5 HASH 哈希 加密 与JAVA 互通
  • (第61天)多租户架构(CDB/PDB)
  • (独孤九剑)--文件系统
  • (翻译)Entity Framework技巧系列之七 - Tip 26 – 28
  • (附源码)springboot建达集团公司平台 毕业设计 141538
  • (附源码)springboot美食分享系统 毕业设计 612231
  • (附源码)计算机毕业设计ssm基于Internet快递柜管理系统
  • (九)信息融合方式简介
  • (算法)Travel Information Center
  • (五)IO流之ByteArrayInput/OutputStream