JUC生产者-消费者

2024-08-27 04:28
文章标签 juc 生产者 消费者

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



http://www.chinasem.cn/article/1110629

相关文章

java线程深度解析(五)——并发模型(生产者-消费者)

http://blog.csdn.net/Daybreak1209/article/details/51378055 三、生产者-消费者模式     在经典的多线程模式中,生产者-消费者为多线程间协作提供了良好的解决方案。基本原理是两类线程,即若干个生产者和若干个消费者,生产者负责提交用户请求任务(到内存缓冲区),消费者线程负责处理任务(从内存缓冲区中取任务进行处理),两类线程之

【编程底层思考】详解Java的JUC多线程并发编程底层组件AQS的作用及原理

Java中的AbstractQueuedSynchronizer(简称AQS)是位于java.util.concurrent.locks包中的一个核心组件,用于构建锁和其他同步器。AQS为实现依赖于FIFO(先进先出)等待队列的阻塞锁和相关同步器提供了一套高效、可扩展的框架。 一、AQS的作用 统一同步状态管理:AQS提供了一个int类型的成员变量state,用于表示同步状态。子类可以根据自己

数字经济时代,零售企业如何实现以消费者为中心的数字化转型?

在数字经济时代,零售企业正面临着前所未有的挑战与机遇。随着消费者行为的数字化和多样化,传统的零售模式已难以满足市场需求。为了在激烈的市场竞争中立于不败之地,零售企业必须实现以消费者为中心的数字化转型。这一转型不仅仅是技术的升级,更是一场涉及企业战略、组织结构、运营模式和人才管理的深刻变革。本文将探讨零售企业在数字化转型过程中遇到的难点,并提出相应的解决策略,通过实际案例分析,展示如何通过综合措施进

生产者消费者模型(能看懂文字就能明白系列)

系列文章目录 能看懂文字就能明白系列 C语言笔记传送门 Java笔记传送门 🌟 个人主页:古德猫宁- 🌈 信念如阳光,照亮前行的每一步 前言 本节目标: 理解什么是阻塞队列,阻塞队列与普通队列的区别理解什么是生产者消费者模型生产者消费者模型的主要作用 一、阻塞队列 阻塞独立是一个特殊的队列,它具有以下特点: 线程安全带有阻塞特性:即如果队列为空,这时继续出队列的话,

三个同步与互斥问题之生产者与消费者

#include<stdio.h> #include<pthread.h> pthread_mutex_t  mutex; #define Max 10 pthread_cond_t pro; pthread_cond_t con; int buffer=0;//全局变量----一开始为0,只有生产者可以执行 void deal_produce(

编写一个生产者消费者模式的JAVA工程

编写一个生产者消费者模式的JAVA工程; 要求: 1)符合生产者消费者模式,避免出现资源访问冲突; 2)输出生产和消费的执行过程; 3)分别统计生产者和消费者的执行时长和等待时长(目前还不知道怎么搞,其他的参考http://blog.csdn.net/monkey_d_meng/article/details/6251879) 创建类Storage,作为仓库 import java.ut

Kafka【十二】消费者拉取主题分区的分配策略

【1】消费者组、leader和follower 消费者想要拉取主题分区的数据,首先必须要加入到一个组中。 但是一个组中有多个消费者的话,那么每一个消费者该如何消费呢,是不是像图中一样的消费策略呢?如果是的话,那假设消费者组中只有2个消费者或有4个消费者,和分区的数量不匹配,怎么办? 所以这里,我们需要学习Kafka中基本的消费者组中的消费者和分区之间的分配规则: 同一个消费者组的消费者都订

Kafka【十三】消费者消费消息的偏移量

偏移量offset是消费者消费数据的一个非常重要的属性。默认情况下,消费者如果不指定消费主题数据的偏移量,那么消费者启动消费时,无论当前主题之前存储了多少历史数据,消费者只能从连接成功后当前主题最新的数据偏移位置读取,而无法读取之前的任何数据。如果想要获取之前的数据,就需要设定配置参数或指定数据偏移量。 【1】起始偏移量 在消费者的配置中,我们可以增加偏移量相关参数auto.offset.re

JAVA并发编程JUC包之CAS原理

在JDK 1.5之后,java api中提供了java.util.concurrent包,简称JUC包。这个包定义了很多我们非常熟悉的工具类,比如原子类AtomicXX,线程池executors、信号量semaphore、阻塞队列、同步器等。日常并发编程要用的熟面孔基本都在这里。        首先,Atomic包,原子操作类,提供了用法简单、性能高效、最重要是线程安全的更新一个变量。支持

【硬刚Java并发】JUC基础(13):简介

本文是对《【硬刚大数据之学习路线篇】从零到大数据专家的学习指南(全面升级版)》的Java并发部分补充。