• 设计模式06---生产者消费者模式


    生产者和消费者指的是两个不同的线程类对象,操作统一资源的情况。具体的操作流程如下:

    (1)生产者负责生成数据,消费者负责取走数据;

    (2)生产者每生产完一组数据之后,消费者就要取走一组数据。

    一. 直白写法

    1. info类

     1 public class Info {
     2     private String name;
     3     private double price;
     4     public String getName() {
     5         return name;
     6     }
     7     public void setName(String name) {
     8         this.name = name;
     9     }
    10     public double getPrice() {
    11         return price;
    12     }
    13     public void setPrice(double price) {
    14         this.price = price;
    15     }
    16 
    17 }
    View Code

    2. 生产者类

     1 public class Producer implements Runnable{
     2     private Info info;
     3     public Producer(Info info) {
     4         this.info=info;
     5     }
     6     
     7     @Override
     8     public void run() {
     9         for(int i=0;i<20;i++) {
    10             if(i%2==0) {
    11                 this.info.setName("铅笔");
    12                 try {
    13                     Thread.sleep(100);
    14                 } catch (InterruptedException e) {
    15                     // TODO Auto-generated catch block
    16                     e.printStackTrace();
    17                 }
    18                 this.info.setPrice(1.0);
    19             }else {
    20                 this.info.setName("LV");
    21                 try {
    22                     Thread.sleep(100);
    23                 } catch (InterruptedException e) {
    24                     // TODO Auto-generated catch block
    25                     e.printStackTrace();
    26                 }
    27                 this.info.setPrice(10000.0);
    28             }
    29         }
    30     }
    31 
    32 }
    View Code

    3. 消费者类

     1 public class Consumer implements Runnable{
     2     private Info info;
     3     public Consumer(Info info) {
     4         this.info=info;
     5     }
     6     @Override
     7     public void run() {
     8         for(int i=0;i<20;i++) {
     9             try {
    10                 Thread.sleep(100);
    11             } catch (InterruptedException e) {
    12                 // TODO Auto-generated catch block
    13                 e.printStackTrace();
    14             }
    15             System.out.println(this.info.getName()+"----"+this.info.getPrice());
    16         }    
    17     }
    18 }
    View Code

    4. 测试类

    1 public class Test {
    2     public static void main(String[] args) {
    3         Info info=new Info();
    4         new Thread(new Producer(info)).start();
    5         new Thread(new Consumer(info)).start();
    6     }
    7 }
    View Code
     1 LV----1.0
     2 铅笔----10000.0
     3 铅笔----1.0
     4 铅笔----10000.0
     5 铅笔----1.0
     6 LV----1.0
     7 铅笔----1.0
     8 LV----10000.0
     9 铅笔----1.0
    10 LV----1.0
    11 铅笔----1.0
    12 LV----10000.0
    13 铅笔----10000.0
    14 LV----10000.0
    15 铅笔----10000.0
    16 LV----10000.0
    17 铅笔----1.0
    18 LV----1.0
    19 铅笔----1.0
    20 LV----10000.0
    View Code

    说明:上面添加了sleep方法,是为了更好的观察分析存在的问题。根据测试结果我们可以知道,有两个很严重的问题。第一个是数据错乱,比如铅笔对应的是10000;第二个是数据重复设置,比如铅笔连着打印了几次。不符合生产一个,然后再消费一个的标准。因此,需要进行改进。

    二. 解决数据错乱

    数据的错乱完全是因为非同步的操作完成的,所以应该使用同步处理。因为取和设置是两个不同的操作,要想进行同步操作,因此需要将它们定义再一个类里面。

    1. info类

     1 public class Info {
     2     private String name;
     3     private double price;
     4     public synchronized void set(String name,double price) {
     5         this.name=name;
     6         try {
     7             Thread.sleep(100);
     8         } catch (InterruptedException e) {
     9             // TODO Auto-generated catch block
    10             e.printStackTrace();
    11         }
    12         this.price=price;
    13     }
    14     
    15     public synchronized void get() {
    16         try {
    17             Thread.sleep(100);
    18         } catch (InterruptedException e) {
    19             // TODO Auto-generated catch block
    20             e.printStackTrace();
    21         }
    22         System.out.println(this.name+"---------"+this.price);
    23     }
    24 
    25 }
    View Code

    2.生产者

     1 public class Producer implements Runnable{
     2     private Info info;
     3     public Producer(Info info) {
     4         this.info=info;
     5     }
     6     
     7     @Override
     8     public void run() {
     9         for(int i=0;i<20;i++) {
    10             if(i%2==0) {
    11                 this.info.set("铅笔", 1.0);
    12             }else {
    13                 this.info.set("LV", 10000.0);
    14             }
    15         }
    16     }
    17 
    18 }
    View Code

    3.消费者

     1 public class Consumer implements Runnable{
     2     private Info info;
     3     public Consumer(Info info) {
     4         this.info=info;
     5     }
     6     @Override
     7     public void run() {
     8         for(int i=0;i<20;i++) {
     9             this.info.get();
    10         }    
    11     }
    12 }
    View Code

    4. 测试类

    1 public class Test {
    2     public static void main(String[] args) {
    3         Info info=new Info();
    4         new Thread(new Producer(info)).start();
    5         new Thread(new Consumer(info)).start();
    6     }
    7 }
    View Code
     1 铅笔---------1.0
     2 铅笔---------1.0
     3 LV---------10000.0
     4 LV---------10000.0
     5 LV---------10000.0
     6 LV---------10000.0
     7 LV---------10000.0
     8 铅笔---------1.0
     9 铅笔---------1.0
    10 铅笔---------1.0
    11 LV---------10000.0
    12 LV---------10000.0
    13 铅笔---------1.0
    14 铅笔---------1.0
    15 铅笔---------1.0
    16 LV---------10000.0
    17 LV---------10000.0
    18 铅笔---------1.0
    19 铅笔---------1.0
    20 铅笔---------1.0
    View Code

    说明:现在数据错乱的问题解决了,但是数据重复设置的问题更严重了。需要进一步解决

    三. 解决重复设置问题

    解决重复设置问题就需要有一个标志位来告诉是该生产还是是该消费。比如下面修改就可以解决:

    1. Info类

     1 package com.test.b;
     2 
     3 public class Info {
     4     private String name;
     5     private double price;
     6     private boolean flag=true;
     7     //flag=true:表示可以生产,但是不可以取走
     8     //flag=false:表示可以取走,但是不可以生产
     9     public synchronized void set(String name,double price) {
    10         //重复进入到了set()方法里面,发现不能够生产,因此需要进行等待
    11         if(flag==false) {
    12             try {
    13                 super.wait();
    14             } catch (InterruptedException e) {
    15                 // TODO Auto-generated catch block
    16                 e.printStackTrace();
    17             }
    18         }
    19         this.name=name;
    20         try {
    21             Thread.sleep(100);
    22         } catch (InterruptedException e) {
    23             // TODO Auto-generated catch block
    24             e.printStackTrace();
    25         }
    26         this.price=price;
    27         this.flag=false;//修改生产标记,说明设置完毕,现在可以进行消费了
    28         super.notify();//唤醒其它等待线程
    29     }
    30     
    31     public synchronized void get() {
    32         if(this.flag==true) {//还没生产呢,但是进入到了get方法里面来,需要等待
    33             try {
    34                 super.wait();
    35             } catch (InterruptedException e) {
    36                 // TODO Auto-generated catch block
    37                 e.printStackTrace();
    38             }
    39         }
    40         try {
    41             Thread.sleep(100);
    42         } catch (InterruptedException e) {
    43             // TODO Auto-generated catch block
    44             e.printStackTrace();
    45         }
    46         System.out.println(this.name+"---------"+this.price);
    47         this.flag=true;//消费完毕,说明现在可以进行生产了
    48         super.notify();
    49     }
    50 
    51 }
    View Code

    2. 生产者

     1 public class Producer implements Runnable{
     2     private Info info;
     3     public Producer(Info info) {
     4         this.info=info;
     5     }
     6     
     7     @Override
     8     public void run() {
     9         for(int i=0;i<20;i++) {
    10             if(i%2==0) {
    11                 this.info.set("铅笔", 1.0);
    12             }else {
    13                 this.info.set("LV", 10000.0);
    14             }
    15         }
    16     }
    17 
    18 }
    View Code

    3. 消费者

     1 public class Consumer implements Runnable{
     2     private Info info;
     3     public Consumer(Info info) {
     4         this.info=info;
     5     }
     6     @Override
     7     public void run() {
     8         for(int i=0;i<20;i++) {
     9             this.info.get();
    10         }    
    11     }
    12 }
    View Code

    4. 测试

    1 public class Test {
    2     public static void main(String[] args) {
    3         Info info=new Info();
    4         new Thread(new Producer(info)).start();
    5         new Thread(new Consumer(info)).start();
    6     }
    7 }
    View Code
     1 铅笔---------1.0
     2 LV---------10000.0
     3 铅笔---------1.0
     4 LV---------10000.0
     5 铅笔---------1.0
     6 LV---------10000.0
     7 铅笔---------1.0
     8 LV---------10000.0
     9 铅笔---------1.0
    10 LV---------10000.0
    11 铅笔---------1.0
    12 LV---------10000.0
    13 铅笔---------1.0
    14 LV---------10000.0
    15 铅笔---------1.0
    16 LV---------10000.0
    17 铅笔---------1.0
    18 LV---------10000.0
    19 铅笔---------1.0
    20 LV---------10000.0
    View Code

    Note:生产者和消费者都需要实现Runnable接口。

  • 相关阅读:
    如何通过 Serverless 技术降低微服务应用资源成本?
    Serverless 对研发效能的变革和创新
    Serverless X OpenKruise 部署效率优化之道
    阿里云 Serverless 再升级,从体验上拉开差距
    2019 年 CNCF 中国云原生调查报告
    不错的的机器学习视频分享
    arcgis for js 4.6加载本地发布好的2维地图
    win8 下删除服务
    arcgis10.2 全套安装教程
    git版本回退
  • 原文地址:https://www.cnblogs.com/Hermioner/p/10029581.html
Copyright © 2020-2023  润新知