JavaEE 初阶篇-生产者与消费者模型(线程通信)

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
 

文章目录

        1.0 生产者与消费者模型概述

        2.0 在生产者与消费者模型中涉及的关键概念

        2.1 缓冲区

        2.2 生产者

        2.3 消费者

        2.4 同步机制

        2.5 线程间通信

        3.0 实现生产者与消费者模型的完整代码


        1.0 生产者与消费者模型概述

        消费者与生产者模型是计算机科学中一个经典的并发编程问题,描述了多个生产者和消费者之间如何共享有限缓冲区的情况。在该模型中,生产者负责生产物品并将其放入共享的缓冲区,而消费者则负责从缓冲区取出物品进行消费。生产者与消费者之间必须进行有效的同步和协调,以避免生产者在缓冲区满时继续生产物品,或消费者在缓冲区为空时尝试消费物品,从而导致竞争条件和数据不一致的问题。

        2.0 在生产者与消费者模型中涉及的关键概念

        缓冲区、生产者、消费者、同步机制和线程间通信。

        2.1 缓冲区

        用于存储生产者生产的物品,以便消费者可以从中取出。缓冲区通常是一个有限的队列或缓冲区,可以存储一定数量的物品。

        实现缓冲区可以用到数组、链表实现。目前用的是循环数组实现缓冲区的功能。可以自定义数组大小,默认大小为 10 。

        循环数组的实现思路,定义三个变量:当前存储的个数 size ,头队列的索引也是取出数据的索引:head 和 尾队列的索引也是放入数据的索引处:tail 。

代码如下:

public class Desk {private final String[] arr;private int size = 0;private int head = 0;private int tail = 0;//有参构造器public Desk(int size) {this.arr = new String[size];}//无参构造器,默认大小为10public Desk(){this.arr = new String[10];}}

        定义了有参和无参两个构造器。将 size 、head 、tail 初始化都为 0 。

        2.2 生产者

        负责向缓冲区中生产物品并放入到其中。生产者在生产物品之前通常会检查缓冲区是否已满,如果已满则需要等待直到有空间可用。

        实现生产者,就是实现一个 put 方法,先判断数组中的 size 与 数组大小关系,若 size >= arr.length 时,先唤醒其他全部线程,然后当前线程则进入等待状态;若 size < arr.length 时,将数据放入到索引为 tail 处的数组位置,接着 tail++ ,tail 加完之后需要判断是否越界了,如果越界了,则需要进行将 tail 重新置为 0 。再来 size++ 操作,最后在再唤醒其他线程,当前线程也就可以进行等待状态了。

代码如下:

    //放入数据public synchronized void put(String data) throws InterruptedException {String name = Thread.currentThread().getName();String putData = name + ",放入一个数据:" + data;if ( ! (size >= arr.length) ){arr[tail] = putData;tail++;if (tail >= arr.length){tail = 0;}size++;System.out.println(name + "成功放入数据:" + data + ",当前数据个数为:" + size + "个");Thread.sleep(1000);this.notifyAll();this.wait();}else {this.notifyAll();this.wait();}}

         为了方便观察,用到了 Thread.sleep() 方法。

        2.3 消费者

        负责从缓冲区中取出物品并进行消费。消费者再消费物品之前常会检查缓冲区是否为空,如果为空则需要等待直到有物品可取。

        消费者的实现也是一个 take() 方法,先判断 siez == 0 ,若成立,则先唤醒其他线程,当前线程则进入等待;若不成立,则获取数组中索引位置为 head 的数据,接着 head++ 处理,紧接着判断 head >= arr.length ,若成立,将 head = 0 处理。再接着 size-- ,最后唤醒其他线程,当前线程则进入等待状态。

代码如下:

    //取出数据public synchronized void take() throws InterruptedException {String name = Thread.currentThread().getName();if ( !(size == 0)){String ret = arr[head];head++;if (head >= arr.length){head = 0;}size--;System.out.println(name + "读取到了:" + ret + ",当前还剩数据个数为:" + size + "个");Thread.sleep(1000);this.notifyAll();this.wait();}else {this.notifyAll();this.wait();}}

        同样,这里也用到了 Thread.sleep() 方法,主要是为了方便观察。

        2.4 同步机制

        用于实现生产者与消费者之间的同步协调。常用的同步机制包括互斥锁、条件变量、信号变量等,以确保生产者和消费者之间的操作发生竞争条件。

        实现中就是用到了 synchronized() 这个关键字。这确保了在多线程环境下,同一时刻只有一个线程可以访问 put() 和 take() 方法中的关键代码块,从而保证了线程安全性。

        2.5 线程间通信

        生产者与消费者通常运行再不同的线程中,它们之间需要通过线程间通信机制进行协作。常用的线程间通信方式包括 wait-notify 机制等。

        在循环中调用 wait() 方法,以避免虚假唤醒问题。在同步块中调用 notifyAll() 方法,以确保线程安全性。

        3.0 实现生产者与消费者模型的完整代码

public class ProducerConsumer {public static void main(String[] args) {Desk desk = new Desk(1);//生产者线程1Thread thread1 = new Thread(()->{try {while (true) {desk.put("华为电脑");}} catch (InterruptedException e) {throw new RuntimeException(e);}},"生产者1");thread1.start();//生产者线程2Thread thread2 = new Thread(()->{try {while (true) {desk.put("小米su7");}} catch (InterruptedException e) {throw new RuntimeException(e);}},"生产者2");thread2.start();//生产者线程3Thread thread3 = new Thread(()->{try {while (true) {desk.put("大疆无人机");}} catch (InterruptedException e) {throw new RuntimeException(e);}},"生产者3");thread3.start();//消费者1Thread thread4 = new Thread(()->{try {while (true) {desk.take();}} catch (InterruptedException e) {throw new RuntimeException(e);}},"消费者1");thread4.start();//消费者2Thread thread5 = new Thread(()->{try {while (true) {desk.take();}} catch (InterruptedException e) {throw new RuntimeException(e);}},"消费者2");thread5.start();}
}
public class Desk {private final String[] arr;private int size = 0;private int head = 0;private int tail = 0;//有参构造器public Desk(int size) {this.arr = new String[size];}//无参构造器,默认大小为10public Desk(){this.arr = new String[10];}//放入数据public synchronized void put(String data) throws InterruptedException {String name = Thread.currentThread().getName();String putData = name + ",放入一个数据:" + data;if ( ! (size >= arr.length) ){arr[tail] = putData;tail++;if (tail >= arr.length){tail = 0;}size++;System.out.println(name + "成功放入数据:" + data + ",当前数据个数为:" + size + "个");Thread.sleep(1000);this.notifyAll();this.wait();}else {this.notifyAll();this.wait();}}//取出数据public synchronized void take() throws InterruptedException {String name = Thread.currentThread().getName();if ( !(size == 0)){String ret = arr[head];head++;if (head >= arr.length){head = 0;}size--;System.out.println(name + "读取到了:" + ret + ",当前还剩数据个数为:" + size + "个");Thread.sleep(1000);this.notifyAll();this.wait();}else {this.notifyAll();this.wait();}}
}

一部分的运行结果:

        通过合理设计和实现生产者与消费者模型,可以有效地避免竞争条件和数据不一致的问题,实现多个生产者和消费者之间的协同工作。在实际应用中,消费者与生产者模型被广泛应用于操作系统、并发编程和分布式系统等领域,是并发编程中重要的基础知识之一。

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

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

相关文章

iOS开发进阶(十三):脚手架创建iOS项目

文章目录 一、前言二、xcode-select 命令三、拓展阅读 一、前言 项目初期&#xff0c;需要搭建项目基本框架&#xff0c;为此离不开辅助工具&#xff0c;即脚手架。当然&#xff0c;IDE也可以实现新建空白项目&#xff0c;但是其新建后的项目结构可能不符合预期设计&#xff0…

AWS-EKS 给其他IAM赋予集群管理权限

AWS EKS 设计了权限管理系统&#xff0c;A用户创建的集群 B用户是看不到并且不能管理和使用kubectl的&#xff0c;所以我们需要共同管理集群时就需要操场共享集群访问给其他IAM用户。 两种方式添加集群控制权限&#xff08;前提&#xff1a;使用有管理权限的用户操作&#xff…

顺序表相关习题

&#x1f308; 个人主页&#xff1a;白子寰 &#x1f525; 分类专栏&#xff1a;python从入门到精通&#xff0c;魔法指针&#xff0c;进阶C&#xff0c;C语言&#xff0c;C语言题集&#xff0c;C语言实现游戏&#x1f448; 希望得到您的订阅和支持~ &#x1f4a1; 坚持创作博文…

Java笔试题总结

HashSet子类依靠()方法区分重复元素。 A toString(),equals() B clone(),equals() C hashCode(),equals() D getClass(),clone() 答案:C 解析: 先调用对象的hashcode方法将对象映射为数组下标,再通过equals来判断元素内容是否相同 以下程序执行的结果是&#xff1a; class X{…

【项目实战经验】DataKit迁移MySQL到openGauss(上)

前言 本文将分享DataKit迁移MySQL到openGauss的项目实战&#xff0c;供广大openGauss爱好者参考。 1. 下载操作系统 https://www.openeuler.org/zh/download https://support.huawei.com/enterprise/zh/doc/EDOC1100332931/1a643956 https://support.huawei.com/enterprise…

CSS基础选择器 小案例复习(画三个小盒子)

&#xff08;大家好&#xff0c;前面我们学习了基础的选择器&#xff0c;俗话说&#xff1a;温故而知新。所以今天我们将通过小案例来复习前面学过的小知识点。另&#xff0c;十分感谢大家对我文章的支持❤️&#xff09; 通过这个案例复习两个地方&#xff1a; 类选择器的使用…

基于ZooKeeper的Kafka分布式集群搭建与集群启动停止Shell脚本

下载Kafka压缩包 下方是Kafka官网下载地址&#xff0c;本文使用Kafka 3.0.0在虚拟机环境中搭建分布式集群。 Apache Kafka Downloads link 虽然在Kafka 2.8.0之后可以使用KRaft模式搭建高可用的集群以提高数据处理效率&#xff0c;但是目前还有许多企业依然使用ZooKeeper搭建K…

uni-app如何实现高性能

这篇文章主要讲解uni-app如何实现高性能的问题&#xff1f; 什么是uni-app&#xff1f; 简单说一下什么是uni-app&#xff0c;uni-app是继承自vue.js&#xff0c;对vue做了轻度定制&#xff0c;并且实现了完整的组件化开发&#xff0c;并且支持多端发布的一种架构&#xff0c…

申请SSL证书

有很多方法可以确保您的网站安全。添加SSL证书可针对恶意攻击提供额外且关键的保护层。 即使网站不接受交易&#xff0c;您仍然需要保护用户的登录详细信息、地址和其他个人信息。 没有SSL证书的网站使用HTTP&#xff08;一种基于文本的协议&#xff09;&#xff0c;这意味着…

红队攻防之PowerShell基础免杀(二)

Get busy living or get busy dying 什么是图片免杀&#xff1f; 答&#xff1a;一般情况下&#xff0c;某些 AV 对图像未执行检测处理。这种情况下&#xff0c;它们可以仅使用有效负载数据来生成新图像&#xff0c;或将有效负载嵌入到现有图像的最低有效字节中&#xff0c;使…

泰坦尼克号幸存者数据分析

泰坦尼克号幸存者数据分析 1、泰坦尼克号数据集2、数据集加载与概览3、泰坦尼克号幸存者数据分析4、哪些人可能成为幸存者&#xff1f; 1、泰坦尼克号数据集 泰坦尼克号的沉没是世界上最严重的海难事故之一&#xff0c;造成了大量的人员伤亡。这是一艘号称当时世界上最大的邮轮…

linux基础8:文件系统

linux基础8&#xff1a;文件系统 1.物理存储->逻辑存储&#xff1a;1.从磁盘到分区&#xff1a;1.基本物理结构2.物理存储结构3.逻辑结构4.操作系统的Io需求&#xff1a;5.分区操作and分组操作&#xff1a; 2.文件系统结构&#xff1a;1.基本结构&#xff1a;2.分组区块&…

【cpp】快速排序优化

标题&#xff1a;【cpp】快速排序 水墨不写bug 正文开始&#xff1a; 快速排序的局限性&#xff1a; 虽然快速排序是一种高效的排序算法&#xff0c;但也存在一些局限性&#xff1a; 最坏情况下的时间复杂度&#xff1a;如果选择的基准元素不合适&#xff0c;或者数组中存在大…

插入代码---四

文章目录 在空白节插入代码构造恶意代码计算函数相对地址修改入口地址返回原地址 扩大节新增节 对与一个程序我们需要在里面添加自己的功能&#xff0c;让程序增加一些我们的函数&#xff0c;那么我们就需要了解PE文件结构&#xff0c;然后对其进行修改&#xff0c;这里我们将基…

位运算-191. 位1的个数- 136. 只出现一次的数字

位1的个数 已解答 简单 相关标签 相关企业 编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中 设置位 的个数&#xff08;也被称为汉明重量&#xff09;。 示例 1&#xff1a; 输入&#xff1a;n 11 输…

SpringBoot+ECharts+Html 地图案例详解

1. 技术点 SpringBoot、MyBatis、thymeleaf、MySQL、ECharts 等 此案例使用的地图是在ECharts社区中查找的&#xff1a;makeapie echarts社区图表可视化案例 2. 准备条件 在mysql中创建数据库echartsdb&#xff0c;数据库中创建表t_location_count表&#xff0c;表中设置两个…

能耗监测管理系统与技术方案

能耗监测管理系统是目前能源管理中重要的技术手段&#xff0c;它通过集成现代监测技术和信息技术&#xff0c;实现对能源消耗的实时监控、数据分析和决策支持&#xff0c;帮助企业或机构实现能源的高效管理和节能降耗。本篇文章将从能耗监测管理系统的组成、关键技术、应用领域…

ubuntu-server部署hive-part1-安装jdk

参照 https://blog.csdn.net/qq_41946216/article/details/134345137 操作系统版本&#xff1a;ubuntu-server-22.04.3 虚拟机&#xff1a;virtualbox7.0 安装jdk 上传解压 以root用户&#xff0c;将jdk上传至/opt目录下 tar zxvf jdk-8u271-linux-x64.tar.gz 配置环境变量…

嵌入式Qt QGridLayout网格布局管理器

一.QGridLayout网格布局管理器 //以行为单位 设置比例系数 void QGridLayout::setRowStretch ( int row, int stretch ) //以列为单位 设置比例系数 void QGridLayout::setColumnStretch ( int column, int stretch ) 实验&#xff1a; Widget.h&#xff1a; #ifndef _WIDGE…

Redis安装说明

Redis安装说明 大多数企业都是基于Linux服务器来部署项目&#xff0c;而且Redis官方也没有提供Windows版本的安装包。因此课程中我们会基于Linux系统来安装Redis. 此处选择的Linux版本为CentOS 7. Redis的官方网站地址&#xff1a;https://redis.io/ 1.单机安装Redis 1.1.…