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

java thread wait notify 线程通讯

package org.example; public class Main { public static void main(String[] args) { Cook c = new Cook(); Foodie f=new Foodie(); c.setName("厨师"); f.setName("吃货"); c.start(); f.start(); } }
package org.example; public class Desk { //0:没有面条; 1:有面条 public static int foodFlag = 0; //总个数 public static int count=10; //锁对象 public static Object lock = new Object(); }
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Cook extends Thread{ @Override public void run() { while (true){ synchronized (Desk.lock){ if(Desk.count ==0){ break; }else{ //判断桌子上是否有食物 while(Desk.foodFlag ==1){ try{ Desk.lock.wait(); } catch (InterruptedException e){ e.printStackTrace(); } } //如果没有,就制作食物 //System.out.println("厨师做了一碗面条"); //修改桌子上的食物状态 Desk.foodFlag =1; //叫醒等待的消费者开吃 Desk.lock.notifyAll(); } } //System.out.println("厨师做了一碗面条 "+Desk.foodFlag); System.out.println("厨师做了一碗面条 "); //厨师做完面条后,还没来得及打印上面这句话,就被吃货线程先抢到锁,执行消费了; //消费后打印上面这句话,Desk.foodFlag是0 } } }
package org.example; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class Foodie extends Thread{ @Override public void run() { while (true){ synchronized (Desk.lock){ if(Desk.count==0){ break; }else{ //先判断桌子上是否有面条 while (Desk.foodFlag ==0){ //如果没有,就等待 try { Desk.lock.wait(); } catch (InterruptedException e) { throw new RuntimeException(e); } } Desk.count--; //如果有,就开吃 System.out.println("吃货在吃面条,还能再吃"+ Desk.count+"碗!!!"); //log.info("吃货在吃面条,还能再吃"+ Desk.count+"碗!!!"); Desk.lock.notifyAll(); Desk.foodFlag =0; } } } } }

其中的一次运行结果(每次运行的结果可能不同):
吃货在吃面条,还能再吃9碗!!!
厨师做了一碗面条
厨师做了一碗面条
吃货在吃面条,还能再吃8碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃7碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃6碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃5碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃4碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃3碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃2碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃1碗!!!
厨师做了一碗面条
吃货在吃面条,还能再吃0碗!!!

分析

时序细节(对应前几次循环):

  1. 初始count=10,foodFlag=0。两个线程启动,假设Foodie先获得锁,发现没面条,wait()并释放锁。

  2. 厨师第一次生产Cook获得锁,设置foodFlag=1notifyAll,退出同步块。此时准备打印,但还未执行打印语句。

  3. 吃货第一次消费Foodie被唤醒,竞争到锁,发现foodFlag=1,消费:count变为 9,打印“还能再吃9碗”,设置foodFlag=0notifyAll,释放锁。

  4. 厨师第一次打印:此时Cook才执行System.out.println,输出“厨师做了一碗面条”。(这条日志对应第一次生产,但打印在消费日志之后)

  5. 厨师第二次生产Cook再次循环,获得锁(因为foodFlag=0),设置foodFlag=1notifyAll,退出同步块,准备打印。

  6. 厨师第二次打印:在Foodie竞争到锁之前,Cook的打印语句先执行,输出第二个“厨师做了一碗面条”。(这条日志对应第二次生产)

  7. 吃货第二次消费Foodie获得锁,消费,打印“还能再吃8碗”。

因此,连续两个厨师日志分别对应第一次生产(但打印延迟到了消费之后)和第二次生产(打印在消费之前)。这不是虚假唤醒,而是打印位置不当导致的日志顺序错乱。业务逻辑本身是正确的(生产一次、消费一次交替进行)。

如何让日志更清晰?

将打印语句移入同步块内,放在foodFlag修改之后,可以保证“生产日志”与“实际生产动作”原子地出现,

http://www.jsqmd.com/news/633228/

相关文章:

  • StructBERT轻量级情感模型部署指南:开源镜像免配置,10分钟跑通中文情绪识别
  • ACE-Guard限制器:终极解决游戏卡顿的完整指南
  • MNE-Python脑电预处理避坑指南:如何高效处理坏道插值与伪迹去除
  • 钢铁行业余热余压利用装备的盈利模型
  • 如何轻松管理跨平台游戏模组:XXMI Launcher终极指南
  • Zotero PDF预览插件终极指南:告别频繁切换,实现高效文献管理
  • 利用LFM2.5-1.2B-Thinking-GGUF构建智能软件测试用例生成器
  • Step3-VL-10B-Base模型操作系统原理实践:资源调度与监控
  • 终极Virtual Kubelet性能优化指南:10个实用调优策略提升大规模容器部署效率
  • 我不是在用 AI 助手,我在把自己的能力沉淀成组织资产前
  • 用Matlab机器人工具箱搞定六轴机械臂建模:从DH参数到3D可视化(附完整代码)
  • 终极指南:如何使用qmc-decoder快速解锁QQ音乐加密文件
  • 深求·墨鉴部署常见问题解决:从环境配置到模型下载的避坑指南
  • WarcraftHelper:魔兽争霸3终极兼容性修复指南
  • Stable Diffusion 3.5功能体验:FP8量化技术实测,速度快质量高
  • M2LOrder模型Node.js环境配置与项目脚手架生成指南
  • 【Python】概述
  • 比迪丽模型数据库课程设计:艺术作品元数据管理系统
  • 提升Docker镜像构建效率的10个秘诀:Docker Buildx和Bake高级构建技巧
  • Nomic-Embed-Text-V2-MoE环境配置详解:Anaconda虚拟环境管理
  • 番茄小说下载器:如何解决数字阅读的三大核心痛点
  • Qwen3-Embedding-4B镜像部署教程:NVIDIA驱动/CUDA/cuDNN版本兼容性避坑指南
  • Windows Defender Remover架构解析:深度剖析系统安全组件移除的实现原理
  • 不用 Tailscale:3 步把 Mac mini 通过 FRP 暴露到公网(稳定开机自启)
  • 一套代码搞定微信+支付宝全端支付:元点Admin 支付系统设计
  • 美胸-年美-造相Z-Turbo新手指南:避开这些坑,让你的AI绘画更顺利
  • 终极Noto Emoji定制指南:3步打造专属个性化emoji字体
  • STM32F103C8T6最小系统板开发入门:Phi-4-mini-reasoning辅助外设驱动编写
  • 手把手教你5分钟部署Nunchaku FLUX.1-dev,小白也能生成惊艳AI图片
  • 辅助驾驶场景应用:如何用视觉定位模型理解道路目标