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


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();}}}});}
    

Java可视化实现生产者消费者问题相关推荐

  1. JAVA线程之生产者消费者问题

    复习下JAVA线程基础知识: 1.线程的状态: 创建状态:创建了线程对象,此时线程有了相应的内存空间和其他资源,但处于不可运行状态. 就绪状态:线程对象调用start()方法启动线程,进入就绪状态,此 ...

  2. Java线程实现生产者—消费者模式

    在这里插入代码片# Java 线程实现生产者-消费者模式 ##思路:实现类似消费者生产者线程之间通讯的功能,每创建一个工人,就让这个工人干活,干一段时间,工人自动消失,然后又去创建一个工人干活: 代码 ...

  3. Java synchronized 实现生产者-消费者模型

    synchronized synchronized (临界资源) {// 访问临界资源的代码 } 上述代码的作用是给临界资源"加锁",其他线程访问临界资源会被阻塞,目的是保证同一时 ...

  4. java多线程之生产者消费者问题

    今天研究了一下Java多线程,根据老师上课讲的和写的,自己写了一下多线程中的经典问题-----生产者消费者经典问题, package producerconsumer; public class Pr ...

  5. java consumed_Java设计模式—生产者消费者模式(阻塞队列实现)

    生产者消费者模式是并发.多线程编程中经典的 真实世界中的生产者消费者模式 生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系.比如一个人正在准备食物(生产者),而另一个人正在吃(消费者) ...

  6. 生产者消费者 java实现_Java生产者消费者的三种实现

    Java生产者消费者是最基础的线程同步问题,java岗面试中还是很容易遇到的,之前没写过多线程的代码,面试中被问到很尬啊,面完回来恶补下.在网上查到大概有5种生产者消费者的写法,分别如下. 用sync ...

  7. java 面试题 生产者 消费者_面试大厂必看!就凭借这份Java多线程和并发面试题,我拿到了字节和美团的offer!...

    最近好多粉丝私信我说在最近的面试中老是被问到多线程和高并发的问题,又对这一块不是很了解,很简单就被面试官给问倒了,被问倒的后果当然就是被刷下去了,因为粉丝要求,我最近也是花了两天时间 给大家整理了这一 ...

  8. Java 线程池 +生产者消费者+MySQL读取300 万条数据

    1.1需求 数据库300 万条用户数据 ,遍历获取所有用户, 各种组合关联, 获取到一个新的json ,存到redis 上. 1.2 难点 数据库比较多, 不可能单线程查询所有的数据到内存. 1.3解 ...

  9. Java线程实现生产者消费者模式

    1 什么是生产者消费者模式 想一个现实生活中的例子,啤酒商---超市---消费者也就是我们,啤酒商生产了啤酒,然后将啤酒销售给了超市,我们消费之又会到超市将啤酒买回来自己喝,那么啤酒商和消费者之间是什 ...

最新文章

  1. linux中wget命令
  2. 二、分布式文件系统HDFS及其简单使用
  3. spring mvc学习(19):cookievalue注解(显示cookie的值,默认必须有值)
  4. 设计模式(17) 访问者模式(VISITOR) C++实现
  5. pytorch实战案例-手写数字分类-全链接模型——深度AI科普团队
  6. c语言数组统计条形图,【D3.js数据可视化系列教程】--(十)更自由的条形图
  7. 为什么java什么意思_java – 什么意思是immutable?
  8. 腾讯广告算法大赛 | 复赛第二周最佳进步奖得主心得分享
  9. 如何在Swift中发出HTTP请求?
  10. 内核启动流程分析----内核启动
  11. iOS怎么做性能测试,看完这个你就懂了
  12. wecp 启动 php,WEPE工具箱VIP版及网络版合盘20171102
  13. java输入数字金额输出汉字大写,java实现数字金额转换成汉字大写金额
  14. wordpress 搭建的博客: 增加网站备案信息
  15. java毕业生设计车辆调度管理系统计算机源码+系统+mysql+调试部署+lw
  16. Umi部署pages多页面访问配置
  17. Django操作数据库
  18. Python+Vue计算机毕业设计教师绩效工资管理l1v8p(源码+程序+LW+部署)
  19. HCIA 交换机原理与ARP协议
  20. 标称型数据和数值型数据_2017-12-9 机器学习(4)-标称型和数值型

热门文章

  1. python 输出文件分隔符_使用Python文件读写,自定义分隔符的方法
  2. 云翌通信联合方位、鼎信在杭州、北京的产品交流会圆满结束
  3. 阿里云短信验证码签名模板创建方法
  4. 分享10个免费H5模版(主题)资源网站
  5. 2018年10月ios最新退款方法 苹果内购退款流程 必看!!!
  6. Linux线上运维经验分享与故障排除技巧-高俊峰-专题视频课程
  7. 计算机学科课程知识体系回顾初步
  8. 开发直播软件必须要用直播系统源码才行
  9. Excel工作表中最常用的10个经典技巧
  10. 根据卡号前6位判断开户银行