生产者消费者问题

生產者消費者問題（英語：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 }