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.hqwc.cn/news/595933.html

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

相关文章

SpringBoot+ECharts+Html 字符云/词云案例详解

1. 技术点 SpringBoot、MyBatis、thymeleaf、MySQL、ECharts 等 2. 准备条件 在mysql中创建数据库echartsdb&#xff0c;数据库中创建表t_comment表&#xff0c;表中设置两个字段word与count&#xff0c;添加表中的数据。如&#xff1a;附件中的 echartsdb.sql 3. SpringBoot…

如何选择和注册域名,域名有什么作用,什么是域名解析?域名的需要多少钱?

大家好欢迎来到易极赞&#xff0c;今天我们来跟大家聊一下“如何选择和注册域名”这个话题。 域名用来做什么&#xff1f; 域名对您的网站至关重要&#xff0c;因为它代表您的品牌名称并充当网站的地址。对于企业主来说&#xff0c;一个令人难忘的域名有助于建立在线形象和客户…

使用神经网络-遗传算法优化神经网络-风电预测故障(BP,GABP,matlab)

本项目是故障预测&#xff0c;不是时序预测&#xff0c;本质还是分类问题 1 数据集介绍 特征文件&#xff1a; 标签文件&#xff1a;共计4个标签&#xff0c;其中大多数都是正常的&#xff0c;其他是3个不正常的类别 2 使用BP网络 2.1 读取数据&#xff0c;然后选择几个…

git可视化工具

Gitkraken GitKraken 是一款专门用于管理和协作Git仓库的图形化界面工具。它拥有友好直观的界面&#xff0c;使得Git的操作变得更加简单易用&#xff0c;尤其适合那些不熟悉Git命令行的开发者。GitKraken提供了丰富的功能&#xff0c;如代码审查、分支管理、仓库克隆、提交、推…

【精品教程】护网HVV实战教程资料合集(持续更新,共20节)

以下是资料目录&#xff0c;如需下载&#xff0c;请前往星球获取&#xff1a; 01-HW介绍.zip 02-HTTP&Burp课程资料.zip 03-信息收集_3.zip 04-SQL注入漏洞_2.zip 05-命令执行漏洞.zip 06-XSS漏洞.zip 07-CSRF.zip 08-中间件漏洞.zip 09-SSRF.zip 10-XXE.zip 11-Java反序列…

【PyQt5篇】多线程

文章目录 &#x1f354;使用QtDesigner进行设计&#x1f6f8;实现多线程&#x1f339;效果&#x1f50e;原因 &#x1f354;使用QtDesigner进行设计 对应的代码btn.ui <?xml version"1.0" encoding"UTF-8"?> <ui version"4.0">&l…

java——文件上传

一、文件上传——简介 文件上传的简介&#xff1a;文件上传是指将本地计算机中的文件传输到网络上的服务器或另一台计算机上的过程。在 Web 开发中&#xff0c;文件上传通常指的是将用户通过 Web 页面提交的文件&#xff08;如图像、文档、音频、视频等&#xff09;传输到服务器…

C和C++内存管理

目录&#xff1a; 一&#xff1a;C和C内存分布 二&#xff1a;C动态内存管理方式 三&#xff1a;C动态内存管理方式 四&#xff1a;operator new与operator delete函数 五&#xff1a;new和delete的实现原理 六&#xff1a;定位new表达式(placement-new) 七&#xff1…

【Linux】进程初步理解

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 冯诺依曼体系结构1.1 认识冯诺依曼体系结构1.2 存储金字塔 2. 操作系统2.1 概念2.2 结构2.3 操作系统的管理 3. 进程3.1 进程描述3.2 Linux下的PCB 4. task_struct本身内部属性4.1 启动4.2 进程的创建方式4.2.1 父…

2024.4.5-[作业记录]-day10-CSS 布局模型(层模型)

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 作业 2024.4.5-学习笔记1 CSS定位1.1 相对定位 relative1.2 绝对定位 absolut…

MATLAB实现数值求解高阶常微分方程组

一、高阶常微分方程组 高阶常微分方程是指包含多个高阶常微分方程的系统。这些方程通常涉及多个未知函数及其高阶导数。解决高阶常微分方程组通常比解决单个高阶常微分方程更为复杂&#xff0c;因为需要同时考虑多个方程和多个未知函数之间的关系。 一般来说&#xff0c;解决…

分类预测 | Matlab实现TCN-BiGRU-Mutilhead-Attention时间卷积双向门控循环单元多头注意力机制多特征分类预测/故障识别

分类预测 | Matlab实现TCN-BiGRU-Mutilhead-Attention时间卷积双向门控循环单元多头注意力机制多特征分类预测/故障识别 目录 分类预测 | Matlab实现TCN-BiGRU-Mutilhead-Attention时间卷积双向门控循环单元多头注意力机制多特征分类预测/故障识别分类效果基本介绍模型描述程序…