生产者消费者问题

生产者消费者问题（英语：Producer-consumer problem），也称有限缓冲问题（英语：Bounded-buffer problem），是一个多进程同步问题的经典案例。该问题描述了共享固定大小缓冲区的两个进程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中，然后重复此过程。与此同时，消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据，消费者也不会在缓冲区中空时消耗数据。
要解决该问题，就必须让生产者在缓冲区满时休眠（要么干脆就放弃数据），等到下次消费者消耗缓冲区中的数据的时候，生产者才能被唤醒，开始往缓冲区添加数据。同样，也可以让消费者在缓冲区空时进入休眠，等到生产者往缓冲区添加数据之后，再唤醒消费者。通常采用进程间通信的方法解决该问题，常用的方法有信号灯法等。如果解决方法不够完善，则容易出现死锁的情况。出现死锁时，两个线程都会陷入休眠，等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。

使用管程的算法

下列伪代码展示的是使用管程来解决生产者消费者问题的办法。由于管程一定能保证互斥，不必特地考虑保护临界区。也就是说，下面这个方法不用修改就可以推广适用于任意数量的生产者和消费者的情况。
 1 monitor ProducerConsumer { 2     int itemCount; 3     condition full; 4     condition empty; 5  6     procedure add(item) { 7         while (itemCount == BUFFER_SIZE) 8             wait(full); 9         putItemIntoBuffer(item);10         itemCount = itemCount + 1;11         if (itemCount == 1)12             notify(empty);13     }14 15     procedure remove() {16         while (itemCount == 0)17             wait(empty);18         item = removeItemFromBuffer();19         itemCount = itemCount - 1;20         if (itemCount == BUFFER_SIZE - 1)21             notify(full);22         return item;23     }24 }25 26 procedure producer() {27     while (true) {28         item = produceItem()29                ProducerConsumer.add(item)30     }31 }32 33 procedure consumer() {34     while (true) {35         item = ProducerConsumer.remove()36                consumeItem(item)37     }38 }
注意代码中while 语句的用法，都是用在测试缓冲区是否已满或空的时候。当存在多个消费者时，有可能造成竞争条件的情况是：某一消费者在一项数据被放入缓冲区中时被唤醒，但是另一消费者已经在管程上等待了一段时间并移除了这项数据。如果 while 语句被改成if，则会出现放入缓冲区的数据项过多，或移除空缓冲区中的元素的情况。

Java 中的例子

1 import java.util.Stack; 2 import java.util.concurrent.atomic.AtomicInteger; 3  4 /** 5     1个生产者3个消费者 生产、消费10次 6  7     @作者 pt 8  9 */10 11 public class ProducerConsumer {12     Stack<Integer> items = new Stack<Integer>();13     final static int NO_ITEMS = 10;14 15     public static void main(String args[]) {16         ProducerConsumer pc = new ProducerConsumer();17         Thread t1 = new Thread(pc.new Producer());18         Consumer consumer  = pc.new Consumer();19         Thread t2 = new Thread(consumer);20         Thread t3 = new Thread(consumer);21         Thread t4 = new Thread(consumer);22         t1.start();23         try {24             Thread.sleep(100);25         } catch (InterruptedException e1) {26             e1.printStackTrace();27         }28         t2.start();29         t3.start();30         t4.start();31         try {32             t2.join();33             t3.join();34             t4.join();35         } catch (InterruptedException e) {36             e.printStackTrace();37         }38     }39 40     class Producer implements Runnable {41         public void produce(int i) {42             System.out.println("Producing " + i);43             items.push(new Integer(i));44         }45 46         @Override47         public void run() {48             int i = 0;49             // 生产10次50             while (i++ < NO_ITEMS) {51                 synchronized (items) {52                     produce(i);53                     items.notifyAll();54                 }55                 try {56                     // 休眠一段时间57                     Thread.sleep(10);58                 } catch (InterruptedException e) {59                 }60             }61         }62     }63 64     class Consumer implements Runnable {65         // consumed计数器允许线程停止66         AtomicInteger consumed = new AtomicInteger();67 68         public void consume() {69             if (!items.isEmpty()) {70                 System.out.println("Consuming " + items.pop());71                 consumed.incrementAndGet();72             }73         }74 75         private boolean theEnd() {76             return consumed.get() >= NO_ITEMS;77         }78 79         @Override80         public void run() {81             while (!theEnd()) {82                 synchronized (items) {83                     while (items.isEmpty() && (!theEnd())) {84                         try {85                             items.wait(10);86                         } catch (InterruptedException e) {87                             Thread.interrupted();88                         }89                     }90                     consume();91                 }92             }93         }94     }95 }