Java可视化实现生产者消费者问题

article/2025/11/10 0:44:02

引言:生产者消费者问题是一个十分经典的多线程问题。为了更加形象地描述这个问题,采用可视化的形式展示此过程。

在这里插入图片描述
在这里插入图片描述

1、问题重述

     生产者消费者问题也称有限缓冲问题。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。

    当我们在思考如何解决这个问题的时候一定要联系实际。实际生活中(不考虑直接点对点模式),工厂生产的商品一般会通过经销商销售,消费者从经销商中购买商品。经销商手中的存货是有限的,同时他需要的商品也是有限的。当他不需要商品的时候,生产商不能再生产更多商品;当他没有存货的时候,消费者无法从他手中购买。这里,经销商就相当于一个缓冲区。

    这种生产者-消费者模式有什么好处呢?使用这种模式,可以让消费者、生产者互相独立,生产者不需要依赖消费者的消费速度,它只关心缓冲区的状况。

2、前期准备

实现方法有四种:
  • wait()和notify()方法
  • await()和signal()方法
  • 阻塞队列方法
  • 管道方法
  • 此教程采用第一种方法。

    2.1 synchronized

        多线程并发存在着线程安全问题,主要在于存在共享数据以及多个线程共同操作共享数据。使用synchronized可以保证线程互斥的访问代码。其原理在于它可以保证方法或者代码块在同一时刻只有一个可以进入到临界区,还可以保证共享变量的内存可见性

        synchronized一般称"同步锁",在修饰代码块的时候需要传入一个对象作为"锁"的对象。线程同步就是利用锁机制先给共享资源上锁,只有拿到锁的线程才可以访问共享资源,其他线程进入等待状态

    2.2 wait()方法

        wait方法()是Object类的方法,其作用是使当前执行代码的线程进入等待。在调用wait()方法之前,线程必须获得该对象的锁,即只能在同步方法或同步块中调用wait()方法。wait()方法执行之后,当前线程释放锁。从wait()返回之前,线程会与其他线程产生资源竞争。当调用wait()时,如果没有锁将会抛出异常

    2.3 notify()方法与notifyAll()方法

        notify()方法用来通知那些可能等待该对象的对象锁的其他线程,若有多个线程,则随机选择wait状态的线程对其notify,使它获得该对象的对象锁。但是,线程不会马上释放锁,而是等到执行notify()方法的线程将程序执行完后即退出synchronized语句块后,该线程才会释放锁。注意,操作不当可能会出现过早通知

        notifyAll()使所有线程退出wait状态

        总之,简单来讲:wait()使线程停止运行,notify()使线程继续运行

    3、实现消费者-生产者模型

    3.1 界面设计

    创建Frame类和UI类,Frame创建窗体,UI添加控件以及绑定事件。该部分只介绍布局,绑定事件后面再细讲。用进度条代表生产/消费进度。界面如下图所示:

    在这里插入图片描述

    class Frame extends JFrame
    {public int width=1500;public int height=1500;UI ui=new UI();public Container container;public Frame(){setTitle("生产者消费者问题");setSize(width,height);setLocation(300,0);		container=getContentPane();container.add(ui);}
    }
    
    class UI extends JPanel
    {public static JProgressBar producerBar;//表示生产者生产进度public static JLabel producerJLabel;//表示生产者生产个数public static JLabel bufferJLabel;//缓冲区public static JLabel amountJLabel;//缓冲区个数public static JProgressBar consumerBar;//表示消费者消费速度public static JLabel consumerJLabel;//表示消费者消费个数JButton bt1;//生产者开始生产JButton bt2;//生产者停止生产JButton bt3;//消费者开始消费JButton bt4;//消费者停止消费public UI(){setLayout(null);setSize(1400,1400);producerBar=new JProgressBar();consumerBar=new JProgressBar();bt1=new JButton("开始生产");bt2=new JButton("停止生产");bt3=new JButton("开始消费");bt4=new JButton("停止消费");producerJLabel=new JLabel("生产者");consumerJLabel=new JLabel("消费者");bufferJLabel=new JLabel("缓冲区 (最大容量25)");amountJLabel=new JLabel("商品数量:0");producerBar.setBackground(Color.WHITE);producerBar.setForeground(Color.BLACK);consumerBar.setBackground(Color.WHITE);producerBar.setForeground(Color.BLACK);bt1.setBounds(50,170,70,60);bt2.setBounds(150,170,70,60);bt3.setBounds(950, 700, 70, 60);bt4.setBounds(1050,700,70,60);bufferJLabel.setBounds(550,350,200,200);amountJLabel.setBounds(550,300,200,400);producerJLabel.setBounds(50,70,50,50);producerBar.setBounds(50, 120, 480, 40);consumerJLabel.setBounds(950,600,50,50);consumerBar.setBounds(950,650,480,40);add(producerBar);add(producerJLabel);add(consumerBar);add(consumerJLabel);add(bt1);add(bt2);add(bt3);add(bt4);add(bufferJLabel);add(amountJLabel);}
    }
    

    3.2 模型设计

    根据问题描述即可知道设计思路,创建三个类分别是:Buffer类(缓冲区)、Producer类(生产者)、Consumer类(消费者)。在Buffer类里面我们要模仿生产者生产商品以及消费者消费商品,这里就产生了共享资源——商品。因此,我们需要用到synchronized锁保证线程安全,采用wait()/notify()方法。注意到一点,在消费者-生产者模式下,生产者在缓冲区未满的情况下只管不断生产,消费者在缓冲区未空的情况下只管不断消费。

    3.2.2 Buffer类

    在Buffer类,我们需要实现的是模拟商品运到仓库和商品从仓库出去以及相应的UI。UI方面,需要实时显示缓冲区商品数量。
    class Buffer
    {private static final int max=25;//缓冲区最大容量private LinkedList<Object>list=new LinkedList<Object>();//表示商品实体JLabel amount;//缓冲区当前商品数量		public Buffer(JLabel amount){this.amount=amount;}public synchronized void produce()//{while(list.size()==max)//当缓冲区达到最大容量时,如果不释放资源,则生产进程一直处于阻塞状态{amount.setText("缓冲区已满,生产阻塞");try {wait();//生产阻塞} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}//没有达到缓冲区最大容量list.add(new Object());//生产商品		amount.setText("商品数量: "+list.size());notifyAll();//当生产一个商品之后,可以唤醒其他线程}public synchronized void consume(){while(list.size()==0)//当缓冲区为空时{amount.setText("缓冲区已空,消费阻塞");try {wait();} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}list.remove();//消费商品amount.setText("商品数量: "+list.size());notifyAll();}
    }
    

    3.2.3 Producer类

    用进度条模拟消费者不断生产的过程
    class Producer implements Runnable
    {Buffer buffer;JProgressBar produceBar;//生产进度条int i;public Producer(Buffer buffer,JProgressBar produceBar) {// TODO Auto-generated constructor stubthis.buffer=buffer;this.produceBar=produceBar;}@Overridepublic void run(){while(true){try {if(i<=25){i++;produceBar.setValue(i*4);try {Thread.sleep(200);} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}buffer.produce();} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}}
    }
    

    3.2.4 Consumer类

    用进度条模拟消费者不断消费的过程
    class Consumer implements Runnable
    {Buffer buffer;	JProgressBar consumeBar;//消费进度条int i;public Consumer(Buffer buffer,JProgressBar consumeBar) {// TODO Auto-generated constructor stubthis.buffer=buffer;this.consumeBar=consumeBar;}@Overridepublic void run(){while(true){try {				if(i<=25){i++;consumeBar.setValue(i*4);try {Thread.sleep(200);} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}buffer.consume();} catch (Exception e) {// TODO: handle exceptione.printStackTrace();}}}
    }
    

    3.2.5 事件绑定

    主要是通过按钮来控制生产/消费的开始与暂停。在UI类的构造函数里面实例化对象,并为按钮绑定监听器。同时申请两个线程,分别控制生产行为和消费行为。
    	Buffer buffer;//缓冲区Producer producer;//生产者Consumer consumer;//消费者Thread thread1;//线程1用于控制生产行为的开始与暂停Thread thread2;//线程2用于控制消费行为的开始与暂停buffer=new Buffer(amountJLabel);public UI(){bt1.addActionListener(new ActionListener() {//开始生产		@Overridepublic void actionPerformed(ActionEvent e) {// TODO Auto-generated method stubif(thread1!=null){try {thread1.stop();} catch (Exception e2) {// TODO: handle exception}}thread1=new Thread(new Producer(buffer,producerBar));thread1.start();}});bt2.addActionListener(new ActionListener() {		@Overridepublic void actionPerformed(ActionEvent e) {// TODO Auto-generated method stubif(thread1!=null){try {thread1.stop();} catch (Exception e2) {// TODO: handle exceptione2.printStackTrace();}}}});bt3.addActionListener(new ActionListener() {			@Overridepublic void actionPerformed(ActionEvent e) {// TODO Auto-generated method stubif(thread2!=null){try {thread2.stop();} catch (Exception e2) {// TODO: handle exceptione2.printStackTrace();}}thread2=new Thread(new Consumer(buffer,consumerBar));thread2.start();}});bt4.addActionListener(new ActionListener() {			@Overridepublic void actionPerformed(ActionEvent e) {// TODO Auto-generated method stubif(thread2!=null){try {thread2.stop();} catch (Exception e2) {// TODO: handle exceptione2.printStackTrace();}}}});}
    

http://chatgpt.dhexx.cn/article/ZrrNbM8v.shtml

相关文章

C语言 生产者消费者问题

目录 生产者消费者问题算法设计实现源程序测试日志总结 生产者消费者问题 算法设计 实现 1.编写所需头文件 #include<stdio.h> #include<Windows.h>2.定义全局变量 #define productor 2 //生产者数目 为2 #define consumers 3 //消费者数目 为3 #define buffe…

操作系统_生产者消费者问题

目录 1&#xff0c;生产者消费者问题 问题的提出 初步思考 进程资源共享关系和同步关系分析 问题的具体解决 第一搏 存在的问题 第二搏 多维度思考 1&#xff0c;单生产者、单消费者、多缓冲区 2&#xff0c;多生产者、多消费者、单缓冲 3&#xff0c;单生产者、单…

超详解“生产者消费者问题”【操作系统】

目录 一.生产者消费者问题&#xff08;问题描述&#xff09; 二.问题分析 三.背景知识 四.代码实现 五.实验结论 一.生产者消费者问题&#xff08;问题描述&#xff09; 有一个生产者在生产产品&#xff0c;这些产品将提供给一个消费者去消费&#xff0c;为了使生产者和消…

生产者消费者问题

文章目录 1.生产者消费者问题1.1 问题描述1.2 问题分析1.3 如何实现1.4 思考① -> ② -> ③③ -> ④ -> ① 1.5 小结 2.多生产者 - 多消费者2.1 问题描述2.2 问题分析2.3 如何实现2.4 小结 1.生产者消费者问题 1.1 问题描述 系统中有一组生产者进程和一组消费者进…

《操作系统》-生产者消费者问题

什么是生产者消费者问题&#xff1f; 系统中有一组生产者进程和一组消费者进程。生产者进程每次生产一个产品放入缓冲区&#xff0c;消费者进程每次从缓冲区中取出一个进程并使用&#xff0c;那么他们之间具有这样一层关系 生产者、消费者共享一个初始为空、大小为n的缓冲区。…

生产者-消费者问题(操作系统)

生产者-消费者问题从特殊到一般(从易到难)可以分3种形式&#xff1a; 一个生产者、一个消费者、一个缓冲区的问题&#xff1b; 一个生产者、一个消费者、n个缓冲区的问题&#xff1b; k个生产者、m个消费者、n个缓冲区的问题&#xff1b; ★当缓冲区空时&#xff0c;生产者可…

Java多线程——生产者消费者问题

创建多个线程去执行不同的任务&#xff0c;如果这些任务之间有着某种关系&#xff0c;那么线程之间必须能够通信来协调完成工作。 生产者消费者问题&#xff08;英语&#xff1a;Producer-consumer problem&#xff09;就是典型的多线程同步案例&#xff0c;它也被称为有限缓冲…

生产者-消费者问题(详解)

目录 1.问题描述 2.问题分析 3.问题实现 3.1 初始化 3.2 生产者 3.3 消费者 1.问题描述 要求如下&#xff1a; 只要缓冲区没满&#xff0c;生产者才能把产品放入缓冲区&#xff0c;否则必须等待。只有缓冲区不空时&#xff0c;消费者才能从中取出产品&#xff0c;否则必…

【操作系统】生产者消费者问题

生产者消费者模型 文章目录 生产者消费者模型 [toc]一、 生产者消费者问题二、 问题分析三、 伪代码实现四、代码实现&#xff08;C&#xff09;五、 互斥锁与条件变量的使用比较 一、 生产者消费者问题 生产者消费者问题&#xff08;英语&#xff1a;Producer-consumer proble…

Sublime Text实现代码自动生成,快速编写HTML/CSS代码

目录 下载Sublime Text安装emmet插件常用自动生成HTML代码实例初始化页面自动补全标签配对自动添加类名和id名自动填充文本内容自动生成同级标签自动生成嵌套标签自动生成提级标签自动生成分组标签自动生成多个元素自动生成带多个属性的元素自动生成隐式标签 常用自动生成CSS代…

MybatisPlus代码自动生成

这里写自定义目录标题 前言一. 什么是 MyBatis-Plus二.MybatisPlus 代码自动生成①idea 插件生成1. 插件2.连接数据源3.生成代码 ②配置工具类生成 前言 最开始&#xff0c;要在 Java 中使用数据库时&#xff0c;需要使用 JDBC&#xff0c;创建 Connection、ResultSet 等&…

Simulink自动代码生成:生成代码的基本设置

Simulink自动代码生成也被称作基于模型开发&#xff08;BMD&#xff09;&#xff0c;相比于传统的手写代码方式能够尽量减少人为错误。模型本身可以用于仿真&#xff0c;单元测试等&#xff0c;更便于提前发现逻辑错误。同时只要约定好模型接口&#xff0c;就可以多人协作&…

C语言代码自动生成工具

一、模型建模模块&#xff1a; 基于开源开发平台Eclipse&#xff0c;以图形方式创建和编辑模型元素&#xff0c;模型元素如下&#xff1a; 活动&#xff1a;初始活动、简单活动、复杂活动、结束活动&#xff1b;状态&#xff1a;初始状态、状态、结束状态&#xff1b;变迁&a…

前端代码自动生成器

场景 1.CodeFun是什么 CodeFun是一款UI 设计稿智能生成源代码的工具,支持微信小程序端、移动端H5和混合APP,上传 Sketch、PSD等形式的设计稿&#xff0c;通过智能化技术一键生成可维护的前端代码. 2.学习成本高吗&#xff1f; 对于前端工程师来说&#xff0c;几乎没有学习成本…

MATLAB/Simulink自动代码生成(一)

Simulink自带了种类繁多、功能强大的模块库&#xff0c;在基于模型设计的开发流程下&#xff0c;Simulink不仅通过仿真可以进行早期设计的验证&#xff0c;还可以生成C/C、PLC等代码直接应用于PC、MCU、DSP等平台。在嵌入式软件开发中发挥着重要的作用&#xff0c;本文以Simuli…

IDEA自动生成代码插件

官方介绍 基于IntelliJ IDEA开发的代码生成插件&#xff0c;支持自定义任意模板&#xff08;Java&#xff0c;html&#xff0c;js&#xff0c;xml&#xff09;。 只要是与数据库相关的代码都可以通过自定义模板来生成。支持数据库类型与java类型映射关系配置。 支持同时生成生…

Matlab/Simulink自动生成C代码实验

目录 0. 概要 1. Matlab /Simulink/Embedded Coder关系与区别 2. 搭建Simulink模型及仿真 2.1 搭建模型 2.2 仿真 3. 生成代码 3.1 求解器设置为定步长 3.2 安装 MinGW-w64 编译器 3.3 调出Simulink Coder 4. 工具都生成了啥呢&#xff1f; 0. 概要 Matlab网站提供了很多…

关于RuoYi自动代码生成功能的使用

为什么要使用代码生成&#xff1f; 答&#xff1a;因为在后端构建的过程中会有许多重复的类似的代码编写&#xff0c;而我们如果一个个去编写&#xff0c;会耗费大量时间与精力&#xff0c;所以我们可以设计一个功能去自动生成这些重复的&#xff0c;简单的代码。而若依系统就…

Mybatis Plus自动生成代码

mybatis-plus自动生成代码 一、简易生成代码二、指定生成的样式&#xff0c;并且不在一个模块1.父pom文件配置2.子模块pom文件配置3.准备vm文件4.设置MyBatisPlusGenerator.java5.运行MyBatisPlusGenerator.java文件6.运行sign-auth模块,解决异常 一、简易生成代码 /*** 代码生…

Simulink自动代码生成:数据类型别名自定义

在手写代码时&#xff0c;我们经常能看到自定义数据类型别名&#xff0c;例如有些代码中将计算机默认的数据类型改为我们自己习惯的名称&#xff0c;如图所示。 目录 一. 系统默认生成的别名二. 建立Simulink AliasType三. 修改Data Type Replacement四. 数据类型别名修改后的…