问:Java 中如何通过代码解决生产消费者问题?
答:
生产者消费者问题是多线程并发的一个经典问题,其问题的核心模型是说有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品,所以在 Java 中解决生产消费者问题的主要原理就是解决并发同步与协作问题,故而常见的解决方案如下:
- 使用 BlockingQueue 解决(推荐),因为其内部已经实现了同步队列,实现的原理是采用 await()和 signal() 方法,其可以在生成对象时指定容量大小,对于生产者容量到达最大时自动阻塞,对于消费者容量为 0 时自动阻塞,样板代码如下(使用 Array 阻塞队列更加OK):
public class CompanyStorage {
private final int MAX_SIZE = 50;
private BlockingQueue<Object> products = new LinkedBlockingQueue<Object>();
//生产商品
public synchronized boolean product(Object src) {
if (products.size() == MAX_SIZE) {
System.out.println("仓库已满,暂时不能继续生产!");
return false;
}
try {
products.put(src);
System.out.println("已生产成功,当前仓库存货量:"+products.size());
return true;
} catch (InterruptedException e) {
System.out.println("生产失败,"+e.getMessage());
}
return false;
}
//消费商品
public synchronized Object consume() {
if (products.isEmpty()) {
System.out.println("仓库已空,暂时不能继续消费!");
return null;
}
try {
Object object = products.take();
System.out.println("已消费成功,当前仓库存货量:"+products.size());
return object;
} catch (InterruptedException e) {
System.out.println("消费失败,"+e.getMessage());
}
return null;
}
}
- 使用 Object 的 wait()/notify() 方法解决,核心原理就是通过并发锁保证仓库集合的原子性,同时通过 wait()/notify() 保证并发协作的有序性,样板代码如下:
public class CompanyStorage {
private final int MAX_SIZE = 50;
private LinkedList<Object> products = new LinkedList<Object>();
//生产产品
public void produce(Object src) {
synchronized (products) {
while (products.size() == MAX_SIZE) {
System.out.println("仓库已满,暂时不能继续生产!");
try {
products.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
products.addFirst(src);
System.out.println("已生产成功,当前仓库存货量:"+products.size());
products.notifyAll();
}
}
// 消费产品
public Object consume() {
synchronized (products) {
while (products.isEmpty()) {
System.out.println("仓库已空,暂时不能继续消费!");
try {
products.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object object = products.removeLast();
System.out.println("已消费成功,当前仓库存货量:"+products.size());
products.notifyAll();
return object;
}
}
}
- 使用 Lock 的 await()/signal() 方法解决,核心原理类似 wait()/notify(),样板代码如下:
public class CompanyStorage {
private final int MAX_SIZE = 50;
private LinkedList<Object> products = new LinkedList<Object>();
private final Lock lock = new ReentrantLock();
private final Condition full = lock.newCondition();
private final Condition empty = lock.newCondition();
//生产产品
public void produce(Object src) {
try {
lock.lock();
while (products.size() == MAX_SIZE) {
System.out.println("仓库已满,暂时不能继续生产!");
try {
full.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
products.addFirst(src);
System.out.println("已生产成功,当前仓库存货量:"+products.size());
empty.signalAll();
} finally {
lock.unlock();
}
}
//消费产品
public Object consume() {
try {
lock.lock();
while (products.isEmpty()) {
System.out.println("仓库已空,暂时不能继续消费!");
try {
empty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Object object = products.remove();
System.out.println("已消费成功,当前仓库存货量:"+products.size());
full.signalAll();
return object;
} finally {
lock.unlock();
}
}
}
- 通过 PipedInputStream/PipedOutputStream 管道缓冲流实现,这种方式基于流,所以对于生产消费者的产品需要首先包装成流,所以不易封装,故而实用性不强。
本文参考自 经典面试题之生产消费者问题解析