生产者-消费者(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个水果
.........
可以看出,多个线程协同工作,按照规则生产和消费水果!