开发者

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

目录
  • 模型概述
  • 完整代码实现
    • 1. Desk.Java(共享数据类)
    • 2. Cook.java(生产者线程)
    • 3. Foodie.java(消费者线程)
    • 4. Main.java(测试类)
  • 关键点解析
    • 1. 同步机制
    • 2. 线程通信
    • 3. 生产消费逻辑
    • 执行结果示例
  • 常见问题解答
    • Q1: 为什么使用notifyAll()而不是notify()?
    • Q2: 为什么要在finally块外调用notifyAll()?
    • Q3: 如何避免死锁?
  • 总结

    本文将介绍如何使用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;
                    }
                    
                    javascript// 如果还有食物未被消费,则等待
                    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()实现线程间协作:

    • wait():释放锁并进入等待状态

    • notifyAll():唤醒所有等待该锁的线程

    3. 生产消费逻辑

    • 生产者检查foodflag

      • 为0时生产食物并设置foodflag=1

      • 为1时等待消费者消费

    • 消费者检查foodflag

      • 为1时消费食物并设置foodflag=0

      • 为0时等待生产者生产

    执行结果示例

    厨师 做了一碗面

    吃货 吃了一碗,还剩 9 碗

    厨师 做了一碗面

    吃货 吃了一碗,还剩 8 碗

    ...

    吃货 吃了一碗,还剩 0 碗

    厨师 停止生产

    吃货 停止消费

    常见问题解答

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

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

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

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

    Q3: 如何避免死锁?

    • 确保每个wait()都有对应的notifyAll()

    • 设置合理的退出条件(如count == 0

    • 避免嵌套锁

    总结

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

    • 如何使用synchronized实现线程同步

    • 如何使用wait()/notifyAll()实现线程通信

    • 生产者-消费者模型的经典实现

    • 多线程编程中的常见问题及解php决方案

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

    到此这篇关于Java生产者和消费者实现等待唤醒机制的文章就介绍到这了,更多相关Java 等待唤醒机制内容编程客栈请搜索编程php客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!

    0

    上一篇:

    下一篇:

    精彩评论

    暂无评论...
    验证码 换一张
    取 消

    最新开发

    开发排行榜