zoukankan      html  css  js  c++  java
  • 生产者消费者模式

    生产者消费者模式是并发、多线程编程中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开发模式,生产者和消费者可以以不同的速度生产和消费数据。这篇文章我们来看看什么是生产者消费者模式,这个问题也是多线程面试题中经常被提及的。如何使用阻塞队列(Blocking Queue)解决生产者消费者模式,以及使用生产者消费者模式的好处。

    真实世界中的生产者消费者模式

    生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系。比如一个人正在准备食物(生产者),而另一个人正在吃(消费者),他们使用一个共用的桌子用于放置盘子和取走盘子,生产者准备食物,如果桌子上已经满了就等待,消费者(那个吃的)等待如果桌子空了的话。这里桌子就是一个共享的对象。在Java Executor框架自身实现了生产者消费者模式它们分别负责添加和执行任务。

    生产者消费者模式的好处

    它的确是一种实用的设计模式,常用于编写多线程或并发代码。下面是它的一些优点:

    1. 它简化的开发,你可以独立地或并发的编写消费者和生产者,它仅仅只需知道共享对象是谁
    2. 生产者不需要知道谁是消费者或者有多少消费者,对消费者来说也是一样
    3. 生产者和消费者可以以不同的速度执行
    4. 分离的消费者和生产者在功能上能写出更简洁、可读、易维护的代码

    多线程中的生产者消费者问题

    生产者消费者问题是一个流行的面试题,面试官会要求你实现生产者消费者设计模式,以至于能让生产者应等待如果队列或篮子满了的话,消费者等待如果队列或者篮子是空的。这个问题可以用不同的方式来现实,经典的方法是使用wait和notify方法在生产者和消费者线程中合作,在队列满了或者队列是空的条件下阻塞,Java5的阻塞队列(BlockingQueue)数据结构更简单,因为它隐含的提供了这些控制,现在你不需要使用wait和nofity在生产者和消费者之间通信了,阻塞队列的put()方法将阻塞如果队列满了,队列take()方法将阻塞如果队列是空的。在下部分我们可以看到代码例子。

    使用阻塞队列实现生产者消费者模式

    阻塞队列实现生产者消费者模式超级简单,它提供开箱即用支持阻塞的方法put()和take(),开发者不需要写困惑的wait-nofity代码去实现通信。BlockingQueue 一个接口,Java5提供了不同的现实,如ArrayBlockingQueue和LinkedBlockingQueue,两者都是先进先出(FIFO)顺序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可选的边界。下面这是一个完整的生产者消费者代码例子,对比传统的wait、nofity代码,它更易于理解。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    import java.util.concurrent.BlockingQueue;
    import java.util.concurrent.LinkedBlockingQueue;
    import java.util.logging.Level;
    import java.util.logging.Logger;
     
    public class ProducerConsumerPattern {
     
        public static void main(String args[]){
     
         //Creating shared object
         BlockingQueue sharedQueue = new LinkedBlockingQueue();
     
         //Creating Producer and Consumer Thread
         Thread prodThread = new Thread(new Producer(sharedQueue));
         Thread consThread = new Thread(new Consumer(sharedQueue));
     
         //Starting producer and Consumer thread
         prodThread.start();
         consThread.start();
        }
     
    }
     
    //Producer Class in java
    class Producer implements Runnable {
     
        private final BlockingQueue sharedQueue;
     
        public Producer(BlockingQueue sharedQueue) {
            this.sharedQueue = sharedQueue;
        }
     
        @Override
        public void run() {
            for(int i=0; i<10; i++){
                try {
                    System.out.println("Produced: " + i);
                    sharedQueue.put(i);
                } catch (InterruptedException ex) {
                    Logger.getLogger(Producer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
     
    }
     
    //Consumer Class in Java
    class Consumer implements Runnable{
     
        private final BlockingQueue sharedQueue;
     
        public Consumer (BlockingQueue sharedQueue) {
            this.sharedQueue = sharedQueue;
        }
     
        @Override
        public void run() {
            while(true){
                try {
                    System.out.println("Consumed: "+ sharedQueue.take());
                } catch (InterruptedException ex) {
                    Logger.getLogger(Consumer.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        }
     
    }
     
    Output:
    Produced: 0
    Produced: 1
    Consumed: 0
    Produced: 2
    Consumed: 1
    Produced: 3
    Consumed: 2
    Produced: 4
    Consumed: 3
    Produced: 5
    Consumed: 4
    Produced: 6
    Consumed: 5
    Produced: 7
    Consumed: 6
    Produced: 8
    Consumed: 7
    Produced: 9
    Consumed: 8
    Consumed: 9

     

    你可以看到生产者线程生产数和消费者线程消费它以FIFO的顺序,因为阻塞队列只允许元素以FIFO的方式来访问。以上就是使用阻塞队列解决生产者消费者问题的全部,我确信它比wait/notify更简单,但你要两者都准备如果你是去面试话。

  • 相关阅读:
    java基础:6.3 封装类、Number类、格式化输出、String
    java 快捷键记录
    java基础:6.2 Object、final、abstract、内部类
    如何解决.so 文件下载到mac 失败的问题
    mac 将.so文件上传到SVN上
    限制输入内容的需求
    Android Intent调用 Uri的使用几种格式
    onItemClick的参数
    Android常见的几种RuntimeException
    android:inputType参数类型说明
  • 原文地址:https://www.cnblogs.com/wych/p/4039742.html
Copyright © 2011-2022 走看看