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

多线程—线程通信之notifyAll()/wait()方法Demo

目标:实现在厨师做完20碗饭后,吃货吃一次,厨师做一次。以此达到20碗饭的动态平衡。

(1)厨师类:
package Test.ProducerAndCustomer;import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;//目标:复刻黑马程序员的厨师吃货Demo://厨师线程,吃货线程,list代表仓库,0代表没面条,1代表有面条public class Producer implements Runnable{Object object = Main.object;@Overridepublic void run() {while (true){synchronized (object) {if (Main.NoodleNum >= 20) {try {object.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}} else {Main.NoodleNum++;System.out.println("厨师已经做了" + Main.NoodleNum + "碗面条!");//叫醒吃货object.notifyAll();}}}}
}
(2)吃货类:
package Test.ProducerAndCustomer;public class Customer implements Runnable{@Overridepublic void run() {Object object = Main.object;while (true){synchronized (object) {if (Main.NoodleNum < 20) {//唤醒厨师继续做:try {object.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}} else {Main.NoodleNum--;System.out.println("吃货现在吃了一碗面!");object.notifyAll();}}}}
}
(3)主函数类:
package Test.ProducerAndCustomer;import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class Main {static int NoodleNum = 0;static Object object = new Object();public static void main(String[] args) {Customer customer = new Customer();Producer producer = new Producer();Thread thCus = new Thread(customer,"吃货");Thread thPro = new Thread(producer,"厨师");thPro.start();thCus.start();}
}

最终运行效果:

在这里插入图片描述
注意:代码逻辑不难,主要在于把控notifyAll()wait() 的动作时机,否则会出现,两个线程都进入等待状态却无人唤醒,导致程序卡死。

犯错点:

在确定同步代码块之前,尝试使用ReentrantLock的实例对象来给代码块加锁。在用这个实例对象调用.lock()/.unlock()方法后,又用它来调用wait(),notify()的方法。导致报出IlegalMonitorStateException的错误

这个错误的源头就是,使用lock()/unlock()等方法后又调用wait()/notify()。
因为后两者是隐式锁,而前两者是显式锁,正确的使用方式应该是使用synchronized()和wait(),notify()来搭配调用。

疑惑:

问:为什么synchronized和wait()/notify()的锁对象需要一致?
答:因为这是java对于线程通信的一种设计,如果前后不一致,则IDEA会如此说:
在这里插入图片描述
当前线程不是(锁的)拥有者。这也就表明,一个线程必须要先持有锁,才能进行wait()/notify()的操作。

联想:

1.如果想要套换到飞机大战的应用场景中,则会是 “保持屏幕当中的飞机数量为20,被打掉或者移出屏幕就会自动补全” 的效果。那到时候,上述代码中的NoodleNum就替换成敌机集合list“厨师”则被替换成生成敌机的代码吃货则被替换成我方飞机,而飞机的减少与补齐的行为则对应上述代码的对NoodelNum的增减操作,无非就是add()/remove()方法的套换。
2.在当今的社会生活中,生产者消费模型作为一种原理性的东西,首先会被应用到消息队列,在此基础上,订单_平台数据采集_处理系统图片视频处理日志处理系统,无处不在,体现出它的广泛性。

生产者消费者模型的特点:

1.解耦性:生产者消费者彼此互不相识,互不干扰,彼此只关心并各自操作共用的那部分。
2.错误隔离:生产者出故障不影响消费者处理已有数据,消费者故障不影响生产者产生数据

食客(消费者)今天没来不代表厨师(生产者)今天不会做饭了,厨师今天打样不代表食客没地方吃饭

3.异步性:生产者和消费者的行为不具有强烈的顺序性和关联性,自由度高。

http://www.dtcms.com/a/395749.html

相关文章:

  • kotlin 常用函数
  • 2025年CSP-J1入门级初赛题解
  • vue3的基本指令以及对js的导入和导出
  • Linux 基础:关机与重启
  • React Native:分享Windows平台搭建react native并构建apk的操作流程和配置信息
  • EC24026露营灯警示灯芯片方案 报警声语音IC 单片机方案开发
  • 反量化的详细过程
  • C语言:实现3x3矩阵对角线求和
  • [Maven 基础课程]Maven 工程继承和聚合
  • 数据库--存储过程
  • mysql默认事务隔离级别下并发读不到最新数据解决方案
  • M3U8通用下载器
  • Vue动态组件详细用法指南
  • C#练习题——委托练习
  • 【TS4】简单的typescript练手项目
  • 前端学习手册-JavaScript函数与回调(十一)
  • Unity小游戏接入抖音敏感词检测
  • 【2025最新】01 Spring Boot 第一个小程序 for VS Code - 通过 Spring Initializr 网站创建
  • 算法面试(3)------YOLO 的核心思想是什么?YOLOv1 到 v8 的演进路线?
  • docker 部署gitlib
  • SpringBoot3.5.5版本大坑
  • Lightroom Classic 2025专业级数字照片管理与后期处理全解析
  • 交叉编译工具链
  • 前端构建工具有哪些?常用前端构建工具推荐、前端构建工具对比与最佳实践
  • 【RocketMQ入门到精通 | 4】工作原理:indexFile索引文件
  • PPIO首发上线DeepSeek-V3.1-Terminus
  • 《嵌入式驱动(一):系统移植》
  • C语言(长期更新)第22讲:文件操作(一)
  • 财务管控——解读79页集团财务业务管控方法及信息化应用案例【附全文阅读】
  • 火语言RPA:解锁开发者工作流的“自动化密码”