生产者消费者问题(Producer-consumer problem),也称有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。生产者生成一定量的数据放到缓冲区中,然后重复此过程;与此同时,消费者也在缓冲区消耗这些数据。生产者和消费者之间必须保持同步,要保证生产者不会在缓冲区满时放入数据,消费者也不会在缓冲区空时消耗数据。不够完善的解决方法容易出现死锁的情况,此时进程都在等待唤醒。
生产者与消费者示意图如下:

问题抛出如下: 妈妈负责做馒头,大林和小林负责吃馒头,厨房有个锅,锅最多放10个馒头,妈妈一共做100个馒头就结束。 使用多线程实现生产者与消费者的多线程例子。统计大林和小林各吃了多少个馒头?
通过分析得知:妈妈线程是生产者线程,大林和小林是消费者线程。锅属于临界资源(缓冲区)。
实现代码如下:
//锅类
/*
* 有限缓冲区
* Stack描述锅。后进先出
*
* */
public class Pot {
private Stack<Integer> pot = new Stack<Integer>();
public static final int MAX_LEN = 10; //锅最多只能放10个馒头。
public static final int MAX_NUMBER =100; //妈妈一共做100个馒头,程序就结束了。
public static int COUNT = 0; //统计现在做了多少个馒头。
private int bigSonNum; //大儿子吃的数量
private int smallSonNum; //小儿子吃的数量
public Stack<Integer> getPot() {
return pot;
}
//做馒头的方法
public synchronized void makeCake(){
COUNT++;
pot.push(COUNT);
System.out.println("妈妈做了第" + COUNT + "个馒头....");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//吃馒头的方法
public synchronized void eatCake(String name){
int temp = pot.pop(); //弹出一个馒头。
System.out.println(name+"吃了第" + temp + "个馒头...");
if("大林".equals(name)){
this.bigSonNum++;
}else{
this.smallSonNum++;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//显示结果
public void showResult(){
System.out.println("大林吃了:" + bigSonNum + "个馒头!");
System.out.println("小林吃了:" + smallSonNum+"个馒头!");
}
}
//生产者,妈妈类
public class Producer implements Runnable {
private Pot pot;
public Producer(Pot pot) {
this.pot = pot;
}
@Override
public void run() {
while(true){
synchronized (pot) {
//不停的做馒头。。。
//判断什么时候就不做了。
if (Pot.COUNT >= 100) {
break;
}
//怎么判断锅填满了呢。
if (pot.getPot().size() == Pot.MAX_LEN) {
//妈妈就进入等待队列了,等待儿子吃馒头。
System.out.println("锅里馒头满了,等待儿子吃馒头....");
try {
//可以换线儿子吃馒头。
pot.notifyAll();
pot.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
//可以换线儿子吃馒头。
pot.notifyAll();
pot.makeCake();
}
}
}
}
}
//消费者类,儿子类
public class Consumer implements Runnable {
private Pot pot;
private String name;
public Consumer(Pot pot,String name) {
this.pot = pot;
this.name = name;
}
@Override
public void run() {
while(true){
synchronized (pot) {
//儿子不停的吃馒头。
//判断什么时候不能了。
if (pot.getPot().size() == 0 && Pot.COUNT >= 100) {
break;
}
//判断锅里有馒头吗?
if (pot.getPot().size() == 0) {
//妈妈就进入等待队列了,等待儿子吃馒头。
System.out.println("锅里没有馒头了,等待妈妈做馒头....");
try {
//可以换醒妈妈做吃馒头。
pot.notifyAll();
//pot.notify();
pot.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
pot.eatCake(this.name);
pot.notifyAll();
}
}
}
}
}
public class Test {
public static void main(String[] args) {
Pot pot = new Pot();//创建锅对象,有限缓冲区。
Producer mother = new Producer(pot);
Consumer bigSon = new Consumer(pot,"大林");
Consumer smallSon = new Consumer(pot,"小林");
Thread motherThread = new Thread(mother);
Thread bigSonThread = new Thread(bigSon);
Thread smallSonThread = new Thread(smallSon);
motherThread.start();
bigSonThread.start();
smallSonThread.start();
try{
motherThread.join(); //等待妈妈线程结束
bigSonThread.join(); //等待大儿子线程结束
smallSonThread.join(); //等待小儿子线程结束
}catch(Exception ex){
ex.printStackTrace();
}
pot.showResult();
}
}
执行结果:
妈妈做了第1个馒头....
妈妈做了第2个馒头....
妈妈做了第3个馒头....
妈妈做了第4个馒头....
妈妈做了第5个馒头....
妈妈做了第6个馒头....
妈妈做了第7个馒头....
妈妈做了第8个馒头....
妈妈做了第9个馒头....
妈妈做了第10个馒头....
锅里馒头满了,等待儿子吃馒头....
小林吃了第10个馒头...
小林吃了第9个馒头...
小林吃了第8个馒头...
小林吃了第7个馒头...
小林吃了第6个馒头...
小林吃了第5个馒头...
小林吃了第4个馒头...
小林吃了第3个馒头...
小林吃了第2个馒头...
小林吃了第1个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第11个馒头....
妈妈做了第12个馒头....
妈妈做了第13个馒头....
妈妈做了第14个馒头....
妈妈做了第15个馒头....
妈妈做了第16个馒头....
妈妈做了第17个馒头....
妈妈做了第18个馒头....
妈妈做了第19个馒头....
妈妈做了第20个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第20个馒头...
大林吃了第19个馒头...
大林吃了第18个馒头...
大林吃了第17个馒头...
大林吃了第16个馒头...
大林吃了第15个馒头...
大林吃了第14个馒头...
大林吃了第13个馒头...
大林吃了第12个馒头...
大林吃了第11个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第21个馒头....
妈妈做了第22个馒头....
妈妈做了第23个馒头....
妈妈做了第24个馒头....
妈妈做了第25个馒头....
妈妈做了第26个馒头....
妈妈做了第27个馒头....
妈妈做了第28个馒头....
妈妈做了第29个馒头....
妈妈做了第30个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第30个馒头...
大林吃了第29个馒头...
大林吃了第28个馒头...
大林吃了第27个馒头...
大林吃了第26个馒头...
大林吃了第25个馒头...
大林吃了第24个馒头...
大林吃了第23个馒头...
大林吃了第22个馒头...
大林吃了第21个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第31个馒头....
妈妈做了第32个馒头....
妈妈做了第33个馒头....
妈妈做了第34个馒头....
妈妈做了第35个馒头....
妈妈做了第36个馒头....
妈妈做了第37个馒头....
妈妈做了第38个馒头....
妈妈做了第39个馒头....
妈妈做了第40个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第40个馒头...
大林吃了第39个馒头...
大林吃了第38个馒头...
大林吃了第37个馒头...
大林吃了第36个馒头...
大林吃了第35个馒头...
大林吃了第34个馒头...
大林吃了第33个馒头...
大林吃了第32个馒头...
大林吃了第31个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第41个馒头....
妈妈做了第42个馒头....
妈妈做了第43个馒头....
妈妈做了第44个馒头....
妈妈做了第45个馒头....
妈妈做了第46个馒头....
妈妈做了第47个馒头....
妈妈做了第48个馒头....
妈妈做了第49个馒头....
妈妈做了第50个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第50个馒头...
大林吃了第49个馒头...
大林吃了第48个馒头...
大林吃了第47个馒头...
大林吃了第46个馒头...
大林吃了第45个馒头...
大林吃了第44个馒头...
大林吃了第43个馒头...
大林吃了第42个馒头...
大林吃了第41个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第51个馒头....
妈妈做了第52个馒头....
妈妈做了第53个馒头....
妈妈做了第54个馒头....
妈妈做了第55个馒头....
妈妈做了第56个馒头....
妈妈做了第57个馒头....
妈妈做了第58个馒头....
妈妈做了第59个馒头....
妈妈做了第60个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第60个馒头...
大林吃了第59个馒头...
大林吃了第58个馒头...
大林吃了第57个馒头...
大林吃了第56个馒头...
大林吃了第55个馒头...
大林吃了第54个馒头...
大林吃了第53个馒头...
大林吃了第52个馒头...
大林吃了第51个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第61个馒头....
妈妈做了第62个馒头....
妈妈做了第63个馒头....
妈妈做了第64个馒头....
妈妈做了第65个馒头....
妈妈做了第66个馒头....
妈妈做了第67个馒头....
妈妈做了第68个馒头....
妈妈做了第69个馒头....
妈妈做了第70个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第70个馒头...
大林吃了第69个馒头...
大林吃了第68个馒头...
大林吃了第67个馒头...
大林吃了第66个馒头...
大林吃了第65个馒头...
大林吃了第64个馒头...
大林吃了第63个馒头...
大林吃了第62个馒头...
大林吃了第61个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第71个馒头....
妈妈做了第72个馒头....
妈妈做了第73个馒头....
妈妈做了第74个馒头....
妈妈做了第75个馒头....
妈妈做了第76个馒头....
妈妈做了第77个馒头....
妈妈做了第78个馒头....
妈妈做了第79个馒头....
妈妈做了第80个馒头....
锅里馒头满了,等待儿子吃馒头....
小林吃了第80个馒头...
小林吃了第79个馒头...
小林吃了第78个馒头...
小林吃了第77个馒头...
小林吃了第76个馒头...
小林吃了第75个馒头...
小林吃了第74个馒头...
小林吃了第73个馒头...
小林吃了第72个馒头...
小林吃了第71个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第81个馒头....
妈妈做了第82个馒头....
妈妈做了第83个馒头....
妈妈做了第84个馒头....
妈妈做了第85个馒头....
妈妈做了第86个馒头....
妈妈做了第87个馒头....
妈妈做了第88个馒头....
妈妈做了第89个馒头....
妈妈做了第90个馒头....
锅里馒头满了,等待儿子吃馒头....
大林吃了第90个馒头...
大林吃了第89个馒头...
大林吃了第88个馒头...
大林吃了第87个馒头...
大林吃了第86个馒头...
大林吃了第85个馒头...
大林吃了第84个馒头...
大林吃了第83个馒头...
大林吃了第82个馒头...
大林吃了第81个馒头...
锅里没有馒头了,等待妈妈做馒头....
锅里没有馒头了,等待妈妈做馒头....
妈妈做了第91个馒头....
妈妈做了第92个馒头....
妈妈做了第93个馒头....
妈妈做了第94个馒头....
妈妈做了第95个馒头....
妈妈做了第96个馒头....
妈妈做了第97个馒头....
妈妈做了第98个馒头....
妈妈做了第99个馒头....
妈妈做了第100个馒头....
大林吃了第100个馒头...
大林吃了第99个馒头...
大林吃了第98个馒头...
大林吃了第97个馒头...
大林吃了第96个馒头...
大林吃了第95个馒头...
大林吃了第94个馒头...
大林吃了第93个馒头...
大林吃了第92个馒头...
大林吃了第91个馒头...
大林吃了:80个馒头!
小林吃了:20个馒头!
1)wait() / notify()方法 上例中我们就是使用了wait() / notify()方式实现。
2)await() / signal()方法
3)BlockingQueue阻塞队列方法
4)信号量
5)管道