java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java 等待唤醒机制

Java生产者和消费者实现等待唤醒机制

作者:宝耶

本文主要介绍了Java生产者和消费者实现等待唤醒机制,通过synchronized、wait()和notifyAll()实现线程间的安全协作,具有一定的参考价值,感兴趣的可以了解一下

本文将介绍如何使用Java多线程实现经典的生产者-消费者模型,通过synchronized、wait()和notifyAll()实现线程间的安全协作。

模型概述

生产者-消费者模型是多线程编程中的经典案例,主要解决以下问题:

完整代码实现

1. Desk.java(共享数据类)

/**
 * 共享数据类
 * - foodflag: 食物状态标记(0=无食物,1=有食物)
 * - count: 剩余食物数量
 * - lock: 同步锁对象
 */
public class Desk {
    public static int foodflag = 0;
    public static int count = 10;
    public static final Object lock = new Object();
}

2. Cook.java(生产者线程)

/**
 * 生产者线程 - 厨师
 */
public class Cook extends Thread {
    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                // 如果食物已全部生产完成,则退出
                if (Desk.count == 0) {
                    break;
                }
                
                // 如果还有食物未被消费,则等待
                if (Desk.foodflag == 1) {
                    try {
                        Desk.lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    // 生产食物
                    System.out.println(Thread.currentThread().getName() + " 做了一碗面");
                    Desk.foodflag = 1;
                    // 通知消费者可以消费了
                    Desk.lock.notifyAll();
                }
            }
        }
        System.out.println(Thread.currentThread().getName() + " 停止生产");
    }
}

3. Foodie.java(消费者线程)

/**
 * 消费者线程 - 吃货
 */
public class Foodie extends Thread {
    @Override
    public void run() {
        while (true) {
            synchronized (Desk.lock) {
                // 如果食物已全部消费完,则退出
                if (Desk.count == 0) {
                    Desk.lock.notifyAll();  // 确保生产者线程能退出
                    break;
                }
                
                // 如果没有食物,则等待
                if (Desk.foodflag == 0) {
                    try {
                        Desk.lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } else {
                    // 消费食物
                    Desk.count--;
                    System.out.println(Thread.currentThread().getName() + " 吃了一碗,还剩 " + Desk.count + " 碗");
                    Desk.foodflag = 0;
                    // 通知生产者可以生产了
                    Desk.lock.notifyAll();
                }
            }
        }
        System.out.println(Thread.currentThread().getName() + " 停止消费");
    }
}

4. Main.java(测试类)

/**
 * 测试类
 */
public class Main {
    public static void main(String[] args) {
        // 创建并启动生产者线程
        Cook cook = new Cook();
        cook.setName("厨师");
        
        // 创建并启动消费者线程
        Foodie foodie = new Foodie();
        foodie.setName("吃货");
        
        cook.start();
        foodie.start();
    }
}

关键点解析

1. 同步机制

我们使用synchronized关键字实现对共享资源的互斥访问:

synchronized (Desk.lock) {
    // 临界区代码
}

2. 线程通信

通过wait()notifyAll()实现线程间协作:

3. 生产消费逻辑

执行结果示例

厨师 做了一碗面
吃货 吃了一碗,还剩 9 碗
厨师 做了一碗面
吃货 吃了一碗,还剩 8 碗
...
吃货 吃了一碗,还剩 0 碗
厨师 停止生产
吃货 停止消费

常见问题解答

Q1: 为什么使用notifyAll()而不是notify()?

notifyAll()会唤醒所有等待线程,让它们公平竞争锁,避免某些线程长期得不到执行的情况。而notify()只随机唤醒一个线程,可能导致线程饥饿。

Q2: 为什么要在finally块外调用notifyAll()?

因为notifyAll()必须在持有锁的情况下调用,而synchronized块结束时锁会自动释放,所以不需要在finally中调用。

Q3: 如何避免死锁?

总结

通过这个案例,我们学习了:

这个模型可以应用于许多实际场景,如消息队列、任务调度等系统设计中。

到此这篇关于Java生产者和消费者实现等待唤醒机制的文章就介绍到这了,更多相关Java 等待唤醒机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文