Java_多线程_生产者消费者模型_互斥锁,阻塞队列

生产者消费者模型(Producer-Consumer Model)是计算机科学中一个经典的并发编程模型,用于解决多线程/多进程环境下的协作问题。

基本概念
生产者:负责生成数据或任务的实体
消费者:负责处理数据或执行任务的实体
缓冲区:生产者与消费者之间共享的数据存储区域
模型特点
生产者与消费者以不同的速度运行
两者通过共享的缓冲区进行通信
缓冲区有大小限制,可能满或空
需要解决的问题
同步问题:
当缓冲区满时,生产者需要等待
当缓冲区空时,消费者需要等待
互斥问题:
对缓冲区的访问必须是互斥的,防止数据竞争
常见实现方式
使用信号量(Semaphore):空缓冲区信号量满缓冲区信号量互斥信号量
使用条件变量(Condition Variable)和互斥锁(Mutex)
使用阻塞队列(高级语言中常用)

本篇我们使用互斥锁和阻塞队列来解决这个问题
在多线程的锁中我们首先要避免的就是死锁这个问题,在 Java 中,Lock 接口及其实现类(如 ReentrantLock)是在 JDK 5(Java 5) 引入的,属于java.util.concurrent.locks 包的一部分。我们这里使用Java的synchronized实现
首先来分析问题,我们可以抽象的将生产者消费者问题想象为,厨师和顾客的问题:
顾客:

  1. 判断桌子上是否有食物
  2. 如果没有就等待
  3. 如果有就直接吃掉
  4. 吃完食物之后,通知厨师继续做食物

厨师:

  1. 判断桌子上是否有食物
  2. 如果桌子上有食物的话就等待
  3. 如果桌子上没有食物的话就制作食物
  4. 将食物放置在桌子上
  5. 唤醒等待的顾客开始吃

分析完毕,首先我们应该先新建三个类分别为:Cook(厨师类),Customer(顾客类),Desk(桌子类)
初始化桌子:

  1. 初始化桌子上食物的标志,0为无食物,1为有食物
  2. 初始化顾客的上限,例如顾客最多吃10份食物
package Thread.Producer_Consumer;public class Desk {public static int FoodFlag=0;//食物当前的状态表示当前桌子上是否有食物public static int count=10;//消费者最多可以吃10个食物public static Object lock=new Object();////创建一个锁对象,用于生产者和消费者线程间的同步
}//由于是所有线程共同的变量所以我们使用static关键字修饰

接下来开始完成顾客线程

package Thread.Producer_Consumer;public class Customer extends  Thread{//消费者线程@Overridepublic void run() {while( true){synchronized(Desk.lock){// 检查是否达到食物上限(count=0表示不能再吃)if(Desk.count==0){break;}else {// 检查桌子上是否有食物(FoodFlag=1表示有食物)if (Desk.FoodFlag == 1) {System.out.println("顾客吃掉食物");Desk.FoodFlag = 0;//表示没有食物Desk.count--;//剩余可吃食物数量减1System.out.println("顾客还可以吃"+Desk.count);Desk.lock.notifyAll();//唤醒lock锁中所有等待的线程} else {//桌上没有食物,则等待try {Desk.lock.wait();//释放锁,并进入等待状态} catch (InterruptedException e) {throw new RuntimeException(e);}}}}}System.out.println("顾客吃饱了,结束消费!");}
}

厨师线程

package Thread.Producer_Consumer;public class Cook extends Thread{@Overridepublic void run() {while (true) {synchronized (Desk.lock){//如果消费者可以吃的食物的数量已经达到最大,那么则直接退出if (Desk.count==0) {break;}else{//如果桌子有食物,等待消费者进程if(Desk.FoodFlag==1){try {Desk.lock.wait();} catch (InterruptedException e) {throw new RuntimeException(e);}}else{//如果桌子没有食物System.out.println("生产者正在生产食物...");//设置桌子有食物Desk.FoodFlag=1;//唤醒消费者线程Desk.lock.notifyAll();}}}}}
}

最后创建一个测试类

package Thread.Producer_Consumer;public class Test {public static void main(String[] args) {Desk desk=new Desk();Customer f1=new Customer();Cook c1=new Cook();f1.setName("消费者1");c1.setName("生产者1");f1.start();c1.start();}
}

在这里插入图片描述
下来我们使用阻塞队列来实现一下:
桌子类:

package Thread.Producer_Consumer_2;public class Desk {public static int count=10;//消费者最多可以吃10个食物public static int Food_max=10;
}

厨师类:

package Thread.Producer_Consumer_2;import java.util.concurrent.ArrayBlockingQueue;public class Cook extends  Thread{ArrayBlockingQueue<String> queue;public Cook(ArrayBlockingQueue<String> queue){this.queue=queue;}//构造方法,创建一个阻塞队列@Overridepublic void run() {//厨师不断将食物放进队列中while(true){if(Desk.Food_max<=0){break;}else{try {queue.put("食物");System.out.println("厨师放了一个食物");Desk.Food_max--;} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
}

顾客类:

package Thread.Producer_Consumer_2;import Thread.Producer_Consumer.Desk;import java.util.concurrent.ArrayBlockingQueue;public class Customer extends  Thread{//消费者线程ArrayBlockingQueue<String> queue;public Customer(ArrayBlockingQueue<String> queue){this.queue=queue;}@Overridepublic void run() {while( true){if(Desk.count==0){System.out.println("顾客吃到上限了");break;}else{try {queue.take();System.out.println("消费者吃掉了一个食物");Desk.count--;} catch (InterruptedException e) {throw new RuntimeException(e);}}}}
}

测试类:

package Thread.Producer_Consumer_2;import Thread.Producer_Consumer.Desk;import java.util.concurrent.ArrayBlockingQueue;public class Test {public static void main(String[] args) {ArrayBlockingQueue<String> list=new ArrayBlockingQueue<>(5);//创建一个阻塞队列Customer f1=new Customer(list);Cook c1=new Cook(list);f1.setName("消费者1");c1.setName("生产者1");f1.start();c1.start();}
}

阻塞队列总结:
在创建阻塞队列的时候,需要创建实现类的对象,我们可以通过查看源码的形式来看一下
在这里插入图片描述
ArrayBlockingQueue实现了BlockingQueue这个接口
在这里插入图片描述
BlockingQueue又继承于Queue
在这里插入图片描述
Queue又继承于Collection
在这里插入图片描述
Collection又继承于Iterable,由此我们也就可以得出,阻塞队列是可以通过for_each循环遍历的
在这里插入图片描述
回归主题,在ArrayBlockingQueue中,有一个带参的构造方法,由此来创建阻塞队列,capacity代表阻塞队列的容量(不要忘记了泛型代表了阻塞队列的参数为哪种类型)
在这里插入图片描述
同时,LinkBlockingQueue也实现了BlockingQueue这个接口
在这里插入图片描述
由此可以得出一个图:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
由于put()和take()方法都是自带锁的,所以我们并不用手动设置锁,同时,由于我们代码中的输出语句在锁的外面在这里插入图片描述
这导致了输出时偶尔并不能按照我们的想法进行输出,但是执行时一定是正确的

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

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

相关文章

Vue3实现视频播放弹窗组件,支持全屏播放,音量控制,进度条自定义样式,适配浏览器小窗播放,视频大小自适配,缓冲loading,代码复制即用

效果图组件所需VUE3代码<template><div class"video-dialog" :class"fullScreen && video-dialog-full-screen"><el-dialogv-model"props.visible"draggable:show-close"false"title""centeralign-c…

LLM层归一化:γβ与均值方差的协同奥秘

LLM层归一化参数均值和方差;缩放和平移参数是什么 层归一化(Layer Normalization,LN)是深度学习中用于稳定神经网络训练的一种归一化技术 均值和方差参数用于对输入数据进行标准化处理,即将输入数据转换为均值为0、方差为1的标准正态分布 缩放因子γ\gammaγ:标准化后…

智慧场景:定制开发开源AI智能名片S2B2C商城小程序赋能零售新体验

摘要&#xff1a;智慧场景作为零售行业创新发展的关键载体&#xff0c;正深刻改变着消费者的生活方式。本文聚焦智慧零售模式下智慧场景的构建&#xff0c;以定制开发开源AI智能名片S2B2C商城小程序为切入点&#xff0c;深入探讨其在零售企业选址布局、商业模式创新、经营理念转…

QML WorkerScript

WorkerScript是QML中实现多线程编程的关键组件&#xff0c;它允许开发者将耗时操作移至后台线程执行&#xff0c;避免阻塞主UI线程&#xff0c;从而提升应用响应速度和用户体验。本文将全面介绍WorkerScript的核心机制、使用方法和最佳实践。WorkerScript核心机制WorkerScript通…

锐浪报表 Grid++Report 表头表尾的隐藏

设计锐浪表格的模板时&#xff0c;可以通过设计多个表头、表尾&#xff0c;表头、表尾中放入打印控件&#xff0c;可以打印相关的数据。在真实打印时&#xff0c;可以通过打印时让表头、表尾隐藏或显示&#xff0c;实现用户的表格样式。一、表头的指定1、 表头可以多个&#xf…

低速信号设计之 QSPI 篇

一、引言​ 在服务器技术不断演进的当下,对高效、稳定的数据存储和传输需求日益增长。QSPI(Quad Serial Peripheral Interface)总线作为一种高速、串行的外围设备接口,在服务器领域中发挥着关键作用。它为服务器中的各类存储设备及部分外围芯片与主处理器之间提供了快速可…

别只知道暴力循环!我从用户名校验功能中领悟到的高效字符集判断法(1684. 统计一致字符串的数目)

别只知道暴力循环&#xff01;我从用户名校验功能中领悟到的高效字符集判断法 &#x1f60e; 大家好&#xff0c;日常开发中&#xff0c;我们经常会遇到一些看似不起眼&#xff0c;却能成为性能瓶颈的小模块。今天&#xff0c;我想和大家分享一个我亲身经历的故事&#xff0c;…

力扣面试150题--在排序数组中查找元素的第一个和最后一个位置

Day 85 题目描述思路 当 nums[mid] < target 时&#xff0c;说明目标值在右侧&#xff0c;移动左指针 left mid 1 当 nums[mid] > target 时&#xff0c;说明目标值可能在当前位置或左侧&#xff0c;移动右指针 right mid - 1 循环结束后&#xff0c;left 指针会指向第…

C++实战:人脸识别7大核心实例

计算机视觉实例应用 基于C++的人脸识别实例 以下是一些基于C++的人脸识别实例的示例和实现方法,涵盖了多种技术和库的应用。这些例子可以帮助开发者快速上手并实现人脸识别功能。 OpenCV 基础人脸检测 使用OpenCV的预训练模型进行人脸检测是入门级示例。OpenCV自带Haar级联…

Uniapp中使用vue3语法

在setup语法糖中调用uniapp的页面生命周期 <script setup>import { onShow } from "dcloudio/uni-app"onShow(() > {//hanlder...}) </script>vue2混入在vue3中建议使用组合式API 新建baseHook.js import { ref } from "vue"; export fu…

C++vector(2)

2.vector深度剖析及模拟实现 2.1std::vector的核心框架接口的模拟实现bit::vector vector的模拟实现 2.2 使用memcpy拷贝问题 假设模拟实现的vector中的reserve接口中&#xff0c;使用memcpy进行的拷贝&#xff0c;以下代码会发生什么问题&#xff1f; int main() {gxl::ve…

IPSec VPN -- 野蛮模式

一、野蛮模式简介野蛮模式VPN是指IPsec VPN中IKE协商采用野蛮模式&#xff08;Aggressive Mode&#xff09;的虚拟专用网络。它是IKE第一阶段协商的一种方式&#xff0c;与主模式相对&#xff0c;具有协商速度快但安全性稍低的特点。以下是具体介绍&#xff1a;1、工作原理&…

rk3588开发板使用硬件编码处理视频

开发板默认下载的ffmpeg是通用版&#xff0c;无法调用rk3588的硬件编码器&#xff0c;视频编码效率低。 nyanmisaka开发了用于jellyfin的ffmpeg&#xff0c;支持rk3588硬件编码器&#xff0c;编译方法&#xff1a; https://github.com/nyanmisaka/ffmpeg-rockchip/wiki/Compil…

`neutron router-gateway-set` 操作失败的可能原因及解决方案

根据提供的错误信息和搜索结果&#xff0c;neutron router-gateway-set 操作失败的可能原因及解决方案如下&#xff1a;一、常见错误原因数据库字符集配置问题&#xff08;中文名支持&#xff09; 表现&#xff1a;若路由器名称包含中文字符&#xff0c;可能因数据库字符集非UT…

(一)ZooKeeper 发展历史

✨博客主页&#xff1a; https://blog.csdn.net/m0_63815035?typeblog &#x1f497;《博客内容》&#xff1a;.NET、Java.测试开发、Python、Android、Go、Node、Android前端小程序等相关领域知识 &#x1f4e2;博客专栏&#xff1a; https://blog.csdn.net/m0_63815035/cat…

OpenCV快速入门之CV宝典

文章目录OpenCV的基础应用一、OpenCV简介&#xff1a;1.1 OpenCV 优势1.2 OpenCV-Python二、环境安装2.1 环境导入三、图像表示3.1 颜色空间&#xff08;Color Space&#xff09;3.2 具体说明3.3 图像在计算机中的表示四、基本图像操作4.1 创建窗口**1. 核心窗口行为控制**cv.W…

LangChain4j 两种类型API

LangChain4j operates on two levels of abstraction: &#xfeff;LangChain4j 提供了两种类型API抽象Low level. At this level, you have the most freedom and access to all the low-level components such as ChatModel, UserMessage, AiMessage, EmbeddingStore, Embedd…

CLI 与 IDE 编码代理比较:提升开发效率的两种路径

引言 在当今快速发展的软件开发领域&#xff0c;人工智能编码助手已成为开发者工具箱中不可或缺的一部分。根据行业报告&#xff0c;使用AI编码助手可以将开发速度提高55%以上&#xff0c;同时显著提升代码质量。目前市场上主要有两种类型的编码代理&#xff1a;集成在IDE中的代…

【STM32】FreeRTOS 任务的创建(二)

这篇文章在于 详细解释 FreeRTOS 中任务的创建过程&#xff0c;包括任务创建的本质过程、API 详解、两种创建方式&#xff08;动态/静态&#xff09;、任务函数规范、常见错误及实践建议。 这里参照&#xff1a;RTOS官方文档&#xff1a;https://www.freertos.org/zh-cn-cmn-s…

软考 系统架构设计师系列知识点之面向服务架构设计理论与实践(9)

接前一篇文章:软考 系统架构设计师系列知识点之面向服务架构设计理论与实践(8) 所属章节: 第15章. 面向服务架构设计理论与实践 第3节 SOA的参考架构 15.3 SOA的参考架构 IBM的Websphere业务集成参考架构(如图15-2所示,以下简称参考架构)是典型的以服务为中心的企业集…