• 设计模式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接口。

  • 相关阅读:
    Windows 平台下的Mysql集群主从复制
    IOS 的loadView 及使用loadView中初始化View注意的问题。(死循环并不可怕)
    【2013625】K2+SAP集成应用解决方案在线研讨会
    to_char 和 to_date 经验分享
    Java向Access插入数据
    spring Bean的生命周期管理
    [置顶] 30分钟,让你成为一个更好的程序员
    Spring框架中Bean的生命周期
    Box2D的相关知识
    第八周项目二
  • 原文地址:https://www.cnblogs.com/Hermioner/p/10029581.html
Copyright © 2020-2023  润新知