Java与Go: 生产者消费者模型

什么是生产者消费者模型

生产者-消费者模型(也称为生产者-消费者问题)是一种常见的并发编程模型,用于处理多线程或多进程之间的协同工作。该模型涉及两个主要角色:生产者和消费者,一个次要角色:缓冲区。

  • 生产者:生产者是生成数据或资源的角色。它将生产的数据或资源放入一个共享缓冲区(如队列)中。

  • 消费者:消费者是消费数据或资源的角色。它从共享缓冲区中获取数据或资源,并进行处理。

生产者和消费者共享一个缓冲区,通过缓冲区进行数据或资源的传递。生产者将数据或资源放入缓冲区,而消费者从缓冲区中取出数据或资源进行处理。

现实案例

例如餐厅订单处理

在一家餐厅中,生产者-消费者模型可以通过厨师(生产者)和服务员(消费者)的角色来表现:

  • 生产者(厨师):厨师负责制作食物。他们接收来自顾客的订单,并开始制作相应的菜肴。制作好的菜肴会放在一个特定的区域(缓冲区),例如出餐台。

  • 消费者(服务员):服务员负责将厨师制作好的菜肴送到顾客的餐桌上。他们从出餐台(缓冲区)中拿取菜肴,并将其送到顾客的桌上。

在这个例子中:

  • 出餐台就像一个共享缓冲区,厨师将制作好的菜肴放在那里,服务员从那里取走。
  • 出餐台有一定的容量限制。厨师在制作新的菜肴之前,需要确保出餐台有足够的空间(缓冲区不满),否则厨师可能会等待一段时间。
  • 服务员在出餐台上拿取菜肴时,也可能遇到出餐台为空的情况。这时,服务员需要等待厨师制作新的菜肴。

通过这个例子,我们可以看到生产者-消费者模型在餐厅中的实际应用。这种模式帮助餐厅协调厨师和服务员之间的工作,从而确保菜肴的制作和服务流程流畅且高效。

问题与解决方案

生产者-消费者模型的主要问题是如何协调生产者和消费者的行为,以避免以下情况:

  • 缓冲区溢出:如果生产者在消费者无法及时消费数据的情况下继续生产,缓冲区可能会变得过满,导致缓冲区溢出。

  • 缓冲区空:如果消费者在生产者无法及时生产数据的情况下继续消费,缓冲区可能会变得空,导致消费者无法继续消费。

为了解决这些问题,生产者和消费者可以使用同步机制,如锁、信号量或条件变量,以确保生产者和消费者在合适的时间进行操作。这些机制可以控制缓冲区的状态,确保生产者和消费者之间的协调工作。

Java实现

可以使用Java 内置的 synchronized 关键字来实现线程同步。通过在共享资源(如 List 缓存区)上使用 synchronized 块或方法,可以确保在操作共享资源时线程的安全性和协调。

import java.util.ArrayList;
import java.util.List;class Producer implements Runnable {private final List<Integer> buffer;private final int maxSize;public Producer(List<Integer> buffer, int maxSize) {this.buffer = buffer;this.maxSize = maxSize;}@Overridepublic void run() {int count = 0;while (true) {synchronized (buffer) {// 如果缓存区满了,等待while (buffer.size() == maxSize) {try {buffer.wait();} catch (InterruptedException e) {Thread.currentThread().interrupt();e.printStackTrace();}}// 生产数据int data = count++;buffer.add(data);System.out.println("Producer produced: " + data);// 唤醒消费者buffer.notify();// 模拟生产数据的时间try {Thread.sleep(500);} catch (InterruptedException e) {Thread.currentThread().interrupt();e.printStackTrace();}}}}
}class Consumer implements Runnable {private final List<Integer> buffer;public Consumer(List<Integer> buffer) {this.buffer = buffer;}@Overridepublic void run() {while (true) {synchronized (buffer) {// 如果缓存区空了,等待while (buffer.isEmpty()) {try {buffer.wait();} catch (InterruptedException e) {Thread.currentThread().interrupt();e.printStackTrace();}}// 从缓存区中取出数据int data = buffer.remove(0);System.out.println("Consumer consumed: " + data);// 唤醒生产者buffer.notify();// 模拟消费数据的时间try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt();e.printStackTrace();}}}}
}public class ProducerConsumerDemo {public static void main(String[] args) {// 创建一个缓存区List<Integer> buffer = new ArrayList<>();int maxSize = 10;// 创建生产者和消费者Producer producer = new Producer(buffer, maxSize);Consumer consumer = new Consumer(buffer);// 创建线程Thread producerThread = new Thread(producer);Thread consumerThread = new Thread(consumer);// 启动线程producerThread.start();consumerThread.start();}
}
  • ProducerConsumer 类在操作 List 缓存区时都使用 synchronized 块来进行线程同步。
  • Producer 类中,如果缓存区满了,生产者线程将等待,直到缓存区有空闲空间;在 Consumer 类中,如果缓存区为空,消费者线程将等待,直到缓存区有数据。
  • 使用 buffer.wait()buffer.notify() 进行线程协调。当生产者或消费者等待时,线程会通过 wait() 进入等待状态;当操作完成后,通过 notify() 唤醒对方线程。

这样可以确保在操作 List 缓存区时线程的安全性和协调。

Go实现

在 Go 语言中,使用 sync.Mutex 来同步单生产者单消费者模型中的共享资源。通过 sync.Mutex,你可以确保在操作共享资源时只有一个 goroutine 能够访问,从而避免竞争条件。

package mainimport ("fmt"sync""time"
)// Producer 负责生产数据并将其放入缓存区
func Producer(buffer *[]int, maxSize int, mu *sync.Mutex, cond *sync.Cond) {count := 0for {mu.Lock()// 如果缓存区满了,等待for len(*buffer) == maxSize {cond.Wait()}// 生产数据data := countcount++*buffer = append(*buffer, data)fmt.Println("Producer produced:", data)// 唤醒消费者cond.Signal()mu.Unlock()// 模拟生产数据的时间time.Sleep(500 * time.Millisecond)}
}// Consumer 负责从缓存区中获取数据并进行消费
func Consumer(buffer *[]int, mu *sync.Mutex, cond *sync.Cond) {for {mu.Lock()// 如果缓存区空了,等待for len(*buffer) == 0 {cond.Wait()}// 从缓存区中获取数据data := (*buffer)[0]*buffer = (*buffer)[1:]fmt.Println("Consumer consumed:", data)// 唤醒生产者cond.Signal()mu.Unlock()// 模拟消费数据的时间time.Sleep(1000 * time.Millisecond)}
}func main() {// 创建一个缓存区buffer := make([]int, 0)maxSize := 10// 创建互斥锁和条件变量var mu sync.Mutexcond := sync.NewCond(&mu)// 创建生产者和消费者 goroutinego Producer(&buffer, maxSize, &mu, cond)go Consumer(&buffer, &mu, cond)// 让 main goroutine 等待time.Sleep(10 * time.Second)
}

在这个示例中:

  • ProducerConsumer 函数操作共享的缓存区 buffer,以及 sync.Mutex 互斥锁 musync.Cond 条件变量 cond

  • ProducerConsumer 函数中,使用 mu.Lock()mu.Unlock() 来确保在操作共享资源时只有一个 goroutine 能访问。

  • Producer 中,如果缓存区满了,生产者线程将调用 cond.Wait() 进入等待状态,直到缓存区有空闲空间。在 Consumer 中,如果缓存区为空,消费者线程将调用 cond.Wait() 进入等待状态,直到缓存区有数据。

  • Producer 生产数据或 Consumer 消费数据后,分别调用 cond.Signal() 唤醒对方线程。

  • main 函数中,通过 go 关键字分别启动 ProducerConsumer goroutine。最后通过 time.Sleep(10 * time.Second)main goroutine 等待 10 秒钟,以便观察生产者和消费者的行为。

这个模型展示了如何使用 sync.Mutexsync.Cond 来同步单生产者单消费者模型中的共享资源,并确保线程安全。

思考

那么要怎么将上述案例改写成多生产者多消费者模型?

往期推荐

Java与Go:字符串转IP地址

Java与Go:文件IO

Java vs. Go:时间函数

Java与Go:字符串方法

Java与Go:方法和接口

Java与Go:引用和指针

Java与Go:对象

Java与Go:Map

Java 与 Go:可变数组

Java 与 Go:数组

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/6492.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

18 内核开发-内核重点数据结构学习

课程简介&#xff1a; Linux内核开发入门是一门旨在帮助学习者从最基本的知识开始学习Linux内核开发的入门课程。该课程旨在为对Linux内核开发感兴趣的初学者提供一个扎实的基础&#xff0c;让他们能够理解和参与到Linux内核的开发过程中。 课程特点&#xff1a; 1. 入门级别&…

办公数据分析利器:Excel与Power Query透视功能

数据分析利器&#xff1a;Excel与Power Query透视功能 Excel透视表和Power Query透视功能是强大的数据分析工具&#xff0c;它们使用户能够从大量数据中提取有意义的信息和趋势&#xff0c;可用于汇总、分析和可视化大量数据。 本文通过示例演示Power Query透视功能的一个小技…

Linux专栏08:Linux基本指令之压缩解压缩指令

博客主页&#xff1a;Duck Bro 博客主页系列专栏&#xff1a;Linux专栏关注博主&#xff0c;后期持续更新系列文章如果有错误感谢请大家批评指出&#xff0c;及时修改感谢大家点赞&#x1f44d;收藏⭐评论✍ Linux基本指令之压缩解压缩指令 编号&#xff1a;08 文章目录 Linu…

Spring Boot与OpenCV:融合机器学习的智能图像与视频处理平台

&#x1f9d1; 作者简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

【模板】二维前缀和

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 二维前缀和板题。 二维前缀和&#xff1a;pre[i][j]a[i][j]pre[i-1][j]pre[i][j-1]-pre[i-1][j-1]; 子矩阵 左上角为(x1,y1) 右下角(x2,y2…

PG控制文件的管理与重建

一.控制文件位置与大小 逻辑位置&#xff1a;pgpobal 表空间中 物理位置&#xff1a;$PGDATA/global/pg_control --pg_global表空间的物理位置就在$PGDATA/global文件夹下 物理大小&#xff1a;8K 二.存放的内容 1.数据库初始化的时候生成的永久化参数&#xff0c;无法更改…

brpc中http2 grpc协议解析

搭建gRPC服务 | bRPC https://blog.csdn.net/INGNIGHT/article/details/132657099 global.cpp http2_rpc_protocol.cpp ParseH2Message解析frame header信息 ParseResult H2Context::ConsumeFrameHead( 这个是固定长度的9字节帧头部&#xff0c;length是&#xff0c;3*8bit…

Mysql技能树学习

查询进阶 别名 MySQL支持在查询数据时为字段名或表名指定别名&#xff0c;指定别名时可以使用AS关键字。 BETWEEN AND条件语句 mysql> SELECT * FROM t_goods WHERE id BETWEEN 6 AND 8; 查询特定数据 &#xff08;CASE&#xff09; select name,case when price <…

Linux 麒麟系统安装

国产麒麟系统官网地址&#xff1a; https://www.openkylin.top/downloads/ 下载该镜像后&#xff0c;使用VMware部署一个虚拟机&#xff1a; 完成虚拟机创建。点击&#xff1a;“开启此虚拟机” 选择“试用试用开放麒麟而不安装&#xff08;T&#xff09;”&#xff0c;进入op…

Cisco Firepower FTD生成troubleshooting File

在出现故障时&#xff0c;需要采集信息 FMC上需要采集对应FTD设备的troubleshooting file system -->health -->monitor 选择相应的FTD&#xff0c;右侧点 generate Generate 4 右上角小红点点开 选择里面的task,就可以看到进度&#xff0c;差不多要10分钟以上 5 完成后…

基于51单片机的交通灯设计—可调时间、夜间模式

基于51单片机的交通灯设计 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.四方向数码管同时显示时间&#xff1b; 2.LED作红、绿、黄灯 3.三个按键可以调整红绿灯时间&#xff1b; 4.夜间模式&am…

IDEA上文件换行符、分隔符(Line Separator)LF,CR,CRLF错乱影响Git上传Github或Gitee代码

IDEA上文件换行符、分隔符(Line Separator)LF&#xff0c;CR&#xff0c;CRLF错乱影响Git上传Github或Gitee代码 指定目录 然后就可以上传了 OK 一定注意更改Line Separator的文件目录 如果是target目录下的文件,是不能修改为LF的,把target文件删除,再重载一次main文件,就…

FFmpeg学习记录(二)—— ffmpeg多媒体文件处理

1.日志系统 常用的日志级别&#xff1a; AV_LOG_ERRORAV_LOG_WARNINGAV_LOG_INFOAV_LOG_DEBUG #include <stdio.h> #include <libavutil/log.h>int main(int argc, char *argv[]) {av_log_set_level(AV_LOG_DEBUG);av_log(NULL, AV_LOG_DEBUG, "hello worl…

【软考高项】三十一、成本管理4个过程

一、规划成本管理 1、定义、作用 定义&#xff1a;确定如何估算、预算、管理、监督和控制项目成本的过程作用&#xff1a;在整个项目期间为如何管理项目成本提供指南和方向 应该在项目规划阶段的早期就对成本管理工作进行规划&#xff0c;建立各成本管理过程的基本框架&…

RKNN Toolkit2 工具的使用

RKNN Toolkit2 是由瑞芯微电子 (Rockchip) 开发的一套用于深度学习模型优化和推理的工具。它主要面向在瑞芯微SoC上进行AI应用开发&#xff0c;但也可以用于PC平台进行模型的转换、量化、推理等操作。它支持将多种深度学习框架的模型&#xff08;如Caffe, TensorFlow, PyTorch等…

单例、工厂、策略、装饰器设计模式

1. 单例模式&#xff08;Singleton Pattern&#xff09;&#xff1a; 单例模式是一种常用的设计模式&#xff0c;用于确保一个类只有一个实例&#xff0c;并提供一个全局访问点。这种模式的特点是类自己负责保存其唯一的实例&#xff0c;并控制其实例化过程。单例模式广泛应用…

【hackmyvm】vivifytech靶机

渗透思路 信息收集端口扫描端口服务信息目录扫描爆破hydra--sshgit提权 信息收集 ┌──(kali㉿kali)-[~] └─$ fping -ag 192.168.9.0/24 2>/dev/null 192.168.9.119 --主机 192.168.9.164 --靶机个人习惯&#xff0c;也方便后续操作&#xff0c;将IP地址赋值给一个变…

【R语言数据分析】卡方检验

目录 交叉卡方检验 配对卡方检验 趋势卡方检验 交叉卡方检验 交叉卡方表用于比较组间“率”的差异。适用于分类型变量&#xff0c;被检验的分类变量应该是无序分类变量&#xff0c;分组变量可以是有序分组也可以是无序分组。比如比较两种药物治疗某个疾病的效率&#xff0c;…

Jhipster8禁用liquibase

开发环境添加dev,no-liquibase&#xff1b;

Stable Diffusion AI绘画

我们今天来了解一下最近很火的SD模型 ✨在人工智能领域&#xff0c;生成模型一直是研究的热点之一。随着深度学习技术的飞速发展&#xff0c;一种名为Stable Diffusion的新型生成模型引起了广泛关注。Stable Diffusion是一种基于概率的生成模型&#xff0c;它可以学习数据的潜…