本文主要是介绍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();}}
}
这篇关于JUC生产者-消费者的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!