JUC生产者-消费者

案例介绍:厨师与传菜员的餐厅协作模拟

这个案例通过一个简化的模拟程序,展示了餐厅中厨师与传菜员之间的协作过程。该程序使用多线程机制,以类比真实世界中厨师烹饪菜品并放入窗口、传菜员从窗口取走菜品并送至餐桌的场景。

场景背景

在餐厅的日常运营中,厨师在后厨负责烹饪菜品,并将制作好的菜品放在一个共享的窗口(或称为传菜口)。传菜员负责从窗口取走菜品并传送到餐桌,以供顾客享用。这一过程需要厨师和传菜员之间的高效配合:厨师需要在窗口有空间时上菜,而传菜员需要及时取走菜品,避免窗口因菜品堆积而阻塞。

程序结构

  • 主类 (ChefAndWaiterExample):负责启动两个线程,一个模拟厨师,另一个模拟传菜员。两者通过共享的 Window 对象进行交互。

  • 窗口类 (Window):代表餐厅中的传菜窗口,作为共享资源,用于存放菜品。它通过一个队列来模拟菜品的堆放,并设置了最大容量,避免窗口被菜品堵塞。

  • 厨师类 (Chef):负责烹饪并将菜品放入窗口。每次厨师制作好一道菜时,会检查窗口是否有空间可放入新菜品。如果窗口已满,厨师将等待传菜员取走菜品后再继续上菜。

  • 传菜员类 (Waiter):负责从窗口中取走菜品。传菜员的职责是及时取走菜品,保证窗口不会因为菜品堆积而导致厨师无法继续上菜。如果窗口为空,传菜员将等待厨师放入新菜品。

练习用代码1

移除了缓冲区里生产和消耗的逻辑

import java.util.LinkedList;
import java.util.Queue;

public class ChefAndWaiterExample {
    public static void main(String[] args) {
        // 创建一个共享的窗口,用于厨师放置菜品和传菜员取走菜品
        Window window = new Window();

        // 创建并启动厨师线程,命名为“厨师”
        Thread chefThread = new Thread(new Chef(window), "厨师");
        chefThread.start();

        // 创建并启动传菜员线程,命名为“传菜员”
        Thread waiterThread = new Thread(new Waiter(window), "传菜员");
        waiterThread.start();
    }
}

// 共享的窗口类,模拟餐厅中厨师和传菜员之间的工作交接点
class Window {
    // 使用队列来模拟菜品的存放,队列中存放的是菜品名称
    private final Queue<String> dishQueue = new LinkedList<>();
    // 最大容量,即窗口中最多可以放置的菜品数量
    private static final int MAX_CAPACITY = 5;

    // 厨师上菜的方法,将菜品放入窗口
    public synchronized void placeDish(String dish) throws InterruptedException {
    }

    // 传菜员取菜的方法,从窗口中取走菜品
    public synchronized String takeDish() throws InterruptedException {
    }
}

// 厨师类,代表负责烹饪并将菜品放入窗口的线程
class Chef implements Runnable {
    private final Window window; // 引用共享的窗口对象

    // 构造函数,接受一个窗口对象
    public Chef(Window window) {
        this.window = window;
    }

    // 厨师线程的执行逻辑
    public void run() {
        int count = 0; // 用于跟踪已生产的菜品数量
        int maxCount = 10; // 定义厨师总共要做的菜品数量
        try {
            // 定义一些菜品的名称,厨师将随机选择一种进行制作
            String[] dishes = {"牛排", "沙拉", "意大利面", "寿司", "汤"};
            while (count <= maxCount) {
                // 随机选择一种菜品并放入窗口
                window.placeDish(dishes[(int) (Math.random() * dishes.length)]);
                count++; // 增加已制作菜品的数量
                Thread.sleep(1000); // 模拟制作菜品所需的时间
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 传菜员类,代表负责从窗口中取走菜品并送到餐桌的线程
class Waiter implements Runnable {
    private final Window window; // 引用共享的窗口对象

    // 构造函数,接受一个窗口对象
    public Waiter(Window window) {
        this.window = window;
    }

    // 传菜员线程的执行逻辑
    public void run() {
        try {
            // 传菜员总共需要取走 10 道菜
            for (int i = 0; i < 10; i++) {
                window.takeDish(); // 从窗口中取走一道菜品
                Thread.sleep(1500); // 模拟传菜到餐桌所需的时间
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

练习用代码2

删除了run方法里的逻辑

import java.util.LinkedList;
import java.util.Queue;

public class ChefAndWaiterExample {
    public static void main(String[] args) {
        // 创建一个共享的窗口,用于厨师放置菜品和传菜员取走菜品
        Window window = new Window();

        // 创建并启动厨师线程,命名为“厨师”
        Thread chefThread = new Thread(new Chef(window), "厨师");
        chefThread.start();

        // 创建并启动传菜员线程,命名为“传菜员”
        Thread waiterThread = new Thread(new Waiter(window), "传菜员");
        waiterThread.start();
    }
}

// 共享的窗口类,模拟餐厅中厨师和传菜员之间的工作交接点
class Window {
    // 使用队列来模拟菜品的存放,队列中存放的是菜品名称
    private final Queue<String> dishQueue = new LinkedList<>();
    // 最大容量,即窗口中最多可以放置的菜品数量
    private static final int MAX_CAPACITY = 5;

    // 厨师上菜的方法,将菜品放入窗口
    public synchronized void placeDish(String dish) throws InterruptedException {
        // 如果窗口中的菜品已达到最大容量,等待传菜员取走菜品
        if (dishQueue.size() >= MAX_CAPACITY) {
            wait(); // 释放锁并等待,直到传菜员取走菜品
        }
        // 厨师将菜品放入窗口并打印消息
        System.out.println(Thread.currentThread().getName() + "上了一道菜:" + dish);
        dishQueue.add(dish); // 将菜品加入队列中
        notify(); // 通知可能正在等待的传菜员线程
    }

    // 传菜员取菜的方法,从窗口中取走菜品
    public synchronized String takeDish() throws InterruptedException {
        // 如果窗口中没有菜品,传菜员等待厨师上菜
        if (dishQueue.isEmpty()) {
            wait(); // 释放锁并等待,直到厨师放置新的菜品
        }
        // 传菜员从窗口中取走菜品并打印消息
        String dish = dishQueue.poll(); // 从队列中取出菜品
        System.out.println(Thread.currentThread().getName() + "取走了一道菜:" + dish);
        notify(); // 通知可能正在等待的厨师线程
        return dish;
    }
}

// 厨师类,代表负责烹饪并将菜品放入窗口的线程
class Chef implements Runnable {
    private final Window window; // 引用共享的窗口对象

    // 构造函数,接受一个窗口对象
    public Chef(Window window) {
        this.window = window;
    }

    // 厨师线程的执行逻辑
    public void run() {
        
    }
}

// 传菜员类,代表负责从窗口中取走菜品并送到餐桌的线程
class Waiter implements Runnable {
    private final Window window; // 引用共享的窗口对象

    // 构造函数,接受一个窗口对象
    public Waiter(Window window) {
        this.window = window;
    }

    // 传菜员线程的执行逻辑
    public void run() {
       
    }
}

代码

import java.util.LinkedList;
import java.util.Queue;

public class ChefAndWaiterExample {
    public static void main(String[] args) {
        // 创建一个共享的窗口,用于厨师放置菜品和传菜员取走菜品
        Window window = new Window();

        // 创建并启动厨师线程,命名为“厨师”
        Thread chefThread = new Thread(new Chef(window), "厨师");
        chefThread.start();

        // 创建并启动传菜员线程,命名为“传菜员”
        Thread waiterThread = new Thread(new Waiter(window), "传菜员");
        waiterThread.start();
    }
}

// 共享的窗口类,模拟餐厅中厨师和传菜员之间的工作交接点
class Window {
    // 使用队列来模拟菜品的存放,队列中存放的是菜品名称
    private final Queue<String> dishQueue = new LinkedList<>();
    // 最大容量,即窗口中最多可以放置的菜品数量
    private static final int MAX_CAPACITY = 5;

    // 厨师上菜的方法,将菜品放入窗口
    public synchronized void placeDish(String dish) throws InterruptedException {
        // 如果窗口中的菜品已达到最大容量,等待传菜员取走菜品
        if (dishQueue.size() >= MAX_CAPACITY) {
            wait(); // 释放锁并等待,直到传菜员取走菜品
        }
        // 厨师将菜品放入窗口并打印消息
        System.out.println(Thread.currentThread().getName() + "上了一道菜:" + dish);
        dishQueue.add(dish); // 将菜品加入队列中
        notify(); // 通知可能正在等待的传菜员线程
    }

    // 传菜员取菜的方法,从窗口中取走菜品
    public synchronized String takeDish() throws InterruptedException {
        // 如果窗口中没有菜品,传菜员等待厨师上菜
        if (dishQueue.isEmpty()) {
            wait(); // 释放锁并等待,直到厨师放置新的菜品
        }
        // 传菜员从窗口中取走菜品并打印消息
        String dish = dishQueue.poll(); // 从队列中取出菜品
        System.out.println(Thread.currentThread().getName() + "取走了一道菜:" + dish);
        notify(); // 通知可能正在等待的厨师线程
        return dish;
    }
}

// 厨师类,代表负责烹饪并将菜品放入窗口的线程
class Chef implements Runnable {
    private final Window window; // 引用共享的窗口对象

    // 构造函数,接受一个窗口对象
    public Chef(Window window) {
        this.window = window;
    }

    // 厨师线程的执行逻辑
    public void run() {
        int count = 0; // 用于跟踪已生产的菜品数量
        int maxCount = 10; // 定义厨师总共要做的菜品数量
        try {
            // 定义一些菜品的名称,厨师将随机选择一种进行制作
            String[] dishes = {"牛排", "沙拉", "意大利面", "寿司", "汤"};
            while (count <= maxCount) {
                // 随机选择一种菜品并放入窗口
                window.placeDish(dishes[(int) (Math.random() * dishes.length)]);
                count++; // 增加已制作菜品的数量
                Thread.sleep(1000); // 模拟制作菜品所需的时间
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 传菜员类,代表负责从窗口中取走菜品并送到餐桌的线程
class Waiter implements Runnable {
    private final Window window; // 引用共享的窗口对象

    // 构造函数,接受一个窗口对象
    public Waiter(Window window) {
        this.window = window;
    }

    // 传菜员线程的执行逻辑
    public void run() {
        try {
            // 传菜员总共需要取走 10 道菜
            for (int i = 0; i < 10; i++) {
                window.takeDish(); // 从窗口中取走一道菜品
                Thread.sleep(1500); // 模拟传菜到餐桌所需的时间
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值