问题描述
假如汽车公司只有一个生产车间, 一间存储仓库(仓库可存储做多n辆车),该公司只生产一种车型。现在需要模仿生产、销售过程。每生产一辆汽车,需要送入仓库临时存储,当仓库满后,停止生产,车辆的销售有一个前提条件是,库存大于零。问题分析
使用面向对象的思维,我们需要考虑类的设计生产车间(生产者):生产者生产汽车放入仓库,这个类包括生产汽车和调入指定库存两个方法
消费者:汽车取出仓库,这个类具有取出汽车的方法
仓库:库存数量,这个类需要统计汽车数量,当库存已满或者库存为空是发出消息
数据结构设计
仓库被设计成栈结构
考虑到汽车生产和销售是两个独立的过程,有必要使用多线程
代码描述
public class ProducersConsumers {
public static void main(String[] args) {
Storage storage = new Storage();
Producer producer = new Producer(storage);
Consumer consumer = new Consumer(storage);
new Thread(producer).start();
new Thread(consumer).start();
}
}
class Car{
int id; //每辆汽车取一个id
public Car(int id) {
this.id = id;
}
}
class Storage{
int index; //用数组实现栈结构
private static final int MAX_NUM = 20;
Car[] carStack = new Car[MAX_NUM];
//入库
public synchronized void push(Car car){
while (index == MAX_NUM){
try {
this.wait(); //仓库满时,入库线程等待
}catch (InterruptedException e){}
}
this.notify(); //有可能空唤醒
carStack[index] = car;
index++;
}
//出库
public synchronized Car pop( ){
while (index == 0){
try {
this.wait(); //库存为空时,出库线程等待
}catch (InterruptedException e){}
}
this.notify();
index--;
return carStack[index];
}
}
class Producer implements Runnable{
Storage storage = null;
//每一辆汽车都会放入仓库,本例只有一个仓库
public Producer(Storage storage) {
this.storage = storage;
}
public void run( ) {
for (int i = 0; i < 20; i++) {
Car car = new Car(i);
storage.push(car);
System.out.println("生产了"+i+"辆汽车");
try {
Thread.sleep(100);
}catch (InterruptedException e){}
}
}
}
class Consumer implements Runnable{
Storage storage = null;
//从仓库取出一辆汽车
public Consumer(Storage storage) {this.storage = storage;}
public void run( ) {
for (int i = 0; i < 20; i++) {
storage.pop();
System.out.println("卖出了"+i+"辆汽车");
try {
Thread.sleep(100);
}catch (InterruptedException e){}
}
}
}
上面的问题中,如果存在多个生产者和多个消费者,上面的问题将变得复杂。在这个问题中,最重要的地方在于有多个进程可以访问公共资源,因此首先要考虑进程的同步问题,不能让两个线程交叉执行(在这个例子中可以理解为,汽车入库和出库必须经过一扇很狭小的门,当然实际中并不是这样)。第二个需要考虑的问题是线程之间的通信问题,表现为 线程的等待和唤醒机制。当然,在实际应用中,任何产生数据的程序都可以称为生产者,而删除数据的一方可被视为消费者。
另外,在实际编码中,恰当使用锁的粒度、使用线程池、使用缓冲队列等都是可以提高的地方。