生産者-消費者(producer-consumer)問題,也稱作有界緩沖區(bounded-buffer)問題,兩個程序共享一個公共的固定大小的緩沖區。其中一個是生産者,用于将消息放入緩沖區;另外一個是消費者,用于從緩沖區中取出消息。問題出現在當緩沖區已經滿了,而此時生産者還想向其中放入一個新的資料項的情形,其解決方法是讓生産者此時進行休眠,等待消費者從緩沖區中取走了一個或者多個資料後再去喚醒它。同樣地,當緩沖區已經空了,而消費者還想去取消息,此時也可以讓消費者進行休眠,等待生産者放入一個或者多個資料時再喚醒它。
生産者消費者模型準确說應該是“生産者-消費者-倉儲”模型,離開了倉儲,生産者消費者模型就顯得沒有說服力了。對于此模型,應該明确一下幾點:
1、生産者僅僅在倉儲未滿時候生産,倉滿則停止生産。
2、消費者僅僅在倉儲有産品時候才能消費,倉空則等待。
3、當消費者發現倉儲沒産品可消費時候會通知生産者生産。
4、生産者在生産出可消費産品時候,應該通知等待的消費者去消費。
接下來我們以生産和消費水果為例來說明此問題:
/**
* 水果類
*/
public class FruitInfo {
private String name;
private String color;
private int weight;
public FruitInfo(String name,String color){
this.name=name;
this.color=color;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
=============================
/*
* 共享類
*/
class CubbyHole {
public final int SIZE=10;//倉庫容量
private String[] nameArr={"蘋果","香蕉","葡萄","橘子","橙子","西瓜","桃子","李子","梨子","木瓜"};
private String[] colorArr={"紅色","黃色","紫紅","黃色","金黃","暗綠","淺綠","淺紫色","淺綠色","淺藍"};
// 倉庫或者叫做緩沖區
private ArrayList<FruitInfo> proList=new ArrayList<FruitInfo>();
/**
* 取資料的同步方法
* @param thName
* @return
*/
public synchronized void get(String thName) {
while (proList.isEmpty()) {
try {
wait(); // 條件不符合,則wait,會釋放對象鎖
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int i=proList.size()-1;
FruitInfo fp=proList.get(i);
proList.remove(i);
notifyAll(); // 通知喚醒其他等待管程的線程
System.out.println(thName + " 消費了倉庫中的第" +(i+1)+"個水果=="+fp.getName()+","+fp.getColor()+
" >倉庫剩餘"+proList.size()+"個水果");
}
/**
* 存放資料的同步方法
* @param thName
*/
public synchronized void put(String thName) {
while (proList.size()==SIZE) {
try {
wait(); // 條件不符合,則wait,會釋放對象鎖
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int i=new Random().nextInt(SIZE);
FruitInfo fp=new FruitInfo(nameArr[i], colorArr[i]);
proList.add(fp);
notifyAll(); // 通知喚醒其他等待管程的線程
System.out.println(thName + " 向倉庫中放進去了一個水果=="+fp.getName()+","+fp.getColor()+
" >倉庫剩餘"+proList.size()+"個水果");
}
/*
* 初始化倉庫中的水果
*/
void init(int num){
FruitInfo fp=null;
for(int i=0;i<num;i++){
fp=new FruitInfo(nameArr[i], colorArr[i]);
proList.add(fp);
}
System.out.println("倉庫中初始庫存水果個數為"+num);
}
}
================================================
class Producer extends Thread { // 生産者線程類
private CubbyHole cubbyhole;
public Producer(CubbyHole c) {
cubbyhole = c;
}
public void run() { // 定義run()方法
for (int i = 0; i < 20; i++) { // 共産生20個
cubbyhole.put(this.getName());
try {
Thread.sleep((long)(Math.random()*2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
=================================
class Consumer extends Thread { // 消費者線程類
private CubbyHole cubbyhole;
public Consumer(CubbyHole c) {
cubbyhole = c;
}
public void run() { // 定義run()方法
for (int i = 0; i <10; i++) {// 消費10個
cubbyhole.get(this.getName());
try {
Thread.sleep((long)(Math.random()*500));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
===============================
public class ProducerConsumerTest { // 主類:測試
public static void main(String[] args) {
CubbyHole c = new CubbyHole();// the shared data object
int num=new Random().nextInt(c.SIZE);
c.init(num);
Producer p1 = new Producer(c); // Producer線程
p1.setName("producer1");
Consumer c1 = new Consumer(c); // Consumer線程
c1.setName("consumer1");
Consumer c2 = new Consumer(c); // 另一個Consumer線程
c2.setName("consumer2");
c1.start(); // 啟動消費者1線程
p1.start(); // 啟動生産者線程
c2.start(); // 啟動消費者2線程
}
}
========================================
運作效果如下所示:
倉庫中初始庫存水果個數為3
producer1 向倉庫中放進去了一個水果==香蕉,黃色 >倉庫剩餘4個水果
consumer1 消費了倉庫中的第4個水果==香蕉,黃色 >倉庫剩餘3個水果
consumer2 消費了倉庫中的第3個水果==葡萄,紫紅 >倉庫剩餘2個水果
consumer2 消費了倉庫中的第2個水果==香蕉,黃色 >倉庫剩餘1個水果
consumer2 消費了倉庫中的第1個水果==蘋果,紅色 >倉庫剩餘0個水果
producer1 向倉庫中放進去了一個水果==蘋果,紅色 >倉庫剩餘1個水果
consumer1 消費了倉庫中的第1個水果==蘋果,紅色 >倉庫剩餘0個水果
producer1 向倉庫中放進去了一個水果==蘋果,紅色 >倉庫剩餘1個水果
consumer1 消費了倉庫中的第1個水果==蘋果,紅色 >倉庫剩餘0個水果
producer1 向倉庫中放進去了一個水果==李子,淺紫色 >倉庫剩餘1個水果
consumer1 消費了倉庫中的第1個水果==李子,淺紫色 >倉庫剩餘0個水果
producer1 向倉庫中放進去了一個水果==李子,淺紫色 >倉庫剩餘1個水果
consumer1 消費了倉庫中的第1個水果==李子,淺紫色 >倉庫剩餘0個水果
producer1 向倉庫中放進去了一個水果==香蕉,黃色 >倉庫剩餘1個水果
consumer1 消費了倉庫中的第1個水果==香蕉,黃色 >倉庫剩餘0個水果
producer1 向倉庫中放進去了一個水果==桃子,淺綠 >倉庫剩餘1個水果
consumer1 消費了倉庫中的第1個水果==桃子,淺綠 >倉庫剩餘0個水果
.........
可以看出,多個線程協同工作,按照規則生産和消費水果!