java thread wait notify 线程通讯

张开发
2026/4/13 8:32:23 15 分钟阅读

分享文章

java thread wait notify 线程通讯
package org.example; public class Main { public static void main(String[] args) { Cook c new Cook(); Foodie fnew 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 count10; //锁对象 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.count0){ 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碗分析时序细节对应前几次循环初始count10,foodFlag0。两个线程启动假设Foodie先获得锁发现没面条wait()并释放锁。厨师第一次生产Cook获得锁设置foodFlag1notifyAll退出同步块。此时准备打印但还未执行打印语句。吃货第一次消费Foodie被唤醒竞争到锁发现foodFlag1消费count变为 9打印“还能再吃9碗”设置foodFlag0notifyAll释放锁。厨师第一次打印此时Cook才执行System.out.println输出“厨师做了一碗面条”。这条日志对应第一次生产但打印在消费日志之后厨师第二次生产Cook再次循环获得锁因为foodFlag0设置foodFlag1notifyAll退出同步块准备打印。厨师第二次打印在Foodie竞争到锁之前Cook的打印语句先执行输出第二个“厨师做了一碗面条”。这条日志对应第二次生产吃货第二次消费Foodie获得锁消费打印“还能再吃8碗”。因此连续两个厨师日志分别对应第一次生产但打印延迟到了消费之后和第二次生产打印在消费之前。这不是虚假唤醒而是打印位置不当导致的日志顺序错乱。业务逻辑本身是正确的生产一次、消费一次交替进行。如何让日志更清晰将打印语句移入同步块内放在foodFlag修改之后可以保证“生产日志”与“实际生产动作”原子地出现

更多文章