消费者和生产者问题,是探究多线程的一道经典题目。它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。
解决生产者/消费者问题的方法可分为两类:
- 采用某种机制保护生产者和消费者之间的同步
- 在生产者和消费者之间建立一个管道。
第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。
在Java中有四种方法支持同步,其中前三个是同步方法,一个是管道方法。
- synchronized / wait() / notify()方法
- Lock / await() / signal()方法
- BlockingQueue阻塞队列方法
- PipedInputStream / PipedOutputStream
本文只介绍前三种。
三种方法的实现
- 先建立一个实体类,可以理解为仓库内存放的产品
Food.java
public class Food {
int id;
public Food(int id){
this.id = id;
}
@Override
public String toString() {
// TODO Auto-generated method stub
return "Food id"+id;
}
}
- 建立仓库,仓库有消费和生产操作
VirtualStack.java
public class VirtualStack {
int index = 0;
Food[] foods = new Food[6];
// 写法一:使用synchronized/wait/notify
// 消费
// public synchronized void pop() {
// while (index == 0) {
// try {
// System.out.println("it is blank");
// this.wait();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// this.notify();
// index--;
// }
// 生产
// public synchronized void push(Food food) {
// while (index == foods.length) {
// try {
// System.out.println("it is full");
// this.wait();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// this.notify();
// foods[index++] = food;
// }
// 方法二:使用Lock/await/signal
// Lock lock = new ReentrantLock();
// Condition not_empty = lock.newCondition();
// Condition not_full = lock.newCondition();
//
// public void pop() {
// lock.lock();
// while (index == 0) {
// try {
// System.out.println("it is blank");
// not_empty.await();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// not_full.signal();
// index--;
// System.out.println("Food -1 --> the rest of foods is" + index);
// lock.unlock();
// }
//
// public void push(Food food) {
// lock.lock();
// while (index == foods.length) {
// try {
// System.out.println("it is full");
// not_full.await();
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
// }
// not_empty.signal();
// foods[index++] = food;
// System.out.println("Food +1 --> the rest of foods is" + index);
// lock.unlock();
// }
int MAX_VALUE = 6;
private LinkedBlockingQueue<Food> list = new LinkedBlockingQueue<Food>(MAX_VALUE);
public void pop(){
if(list.size()==0){
System.out.println("it is empty");
}
try {
list.take();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Food -1 --> the rest of foods is" + list.size());
}
public void push(Food food){
if(list.size()==MAX_VALUE){
System.out.println("it is full");
}
try {
list.put(food);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("Food +1 --> the rest of foods is" + list.size());
}
}
只展示第三种方法,方法一二在注释中有体现
- 建立Producer和Consumer线程,对仓库进行操作
Consumer.java
public class Consumer implements Runnable {
VirtualStack vs = null;
public Consumer(VirtualStack vs) {
// TODO Auto-generated constructor stub
this.vs = vs;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<10;i++){
vs.pop();
// 消费一次用4s
try {
Thread.sleep(4*1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
Producer.java
public class Producer implements Runnable {
VirtualStack vs = null;
public Producer(VirtualStack vs) {
// TODO Auto-generated constructor stub
this.vs = vs;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i = 0;i<10;i++){
Food single = new Food(i);
vs.push(single);
// 生产一次用1s
try {
Thread.sleep(1*1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
- 最后调用生产者和消费者线程
TestProducerConsumer.java
public class TestProducerConsumer {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
VirtualStack vs = new VirtualStack();
Producer prod = new Producer(vs);
Consumer cons = new Consumer(vs);
new Thread(prod).start();
new Thread(cons).start();
}
}
- 结果展示
it is empty
Food +1 --> the rest of foods is1
Food -1 --> the rest of foods is0
Food +1 --> the rest of foods is1
Food +1 --> the rest of foods is2
Food +1 --> the rest of foods is3
Food -1 --> the rest of foods is2
Food +1 --> the rest of foods is3
Food +1 --> the rest of foods is4
Food +1 --> the rest of foods is5
Food +1 --> the rest of foods is6
Food -1 --> the rest of foods is5
Food +1 --> the rest of foods is6
it is full
Food -1 --> the rest of foods is5
Food +1 --> the rest of foods is6
Food -1 --> the rest of foods is5
Food -1 --> the rest of foods is4
Food -1 --> the rest of foods is3
Food -1 --> the rest of foods is2
Food -1 --> the rest of foods is1
Food -1 --> the rest of foods is0