本文参考于NPU_Li Meng的文章《Java多种方式解决生产者消费者问题(十分详细)》和MONKEY_D_MENG的《生产者/消费者问题的多种Java实现方式》
生产消费问题,也称有限缓冲问题(Bounded-buffer problem),是多线程同步问题的经典案例。有一块缓冲区作为仓库,生产者循环生成产品(数据)并放到仓库(缓冲区)中,直到仓库满;与此同时,消费者也在循环从仓库(缓冲区)中消耗这些产品(数据),直到缓冲区空。生产者和消费者之间必须保持同步,要保证生产者不会在缓冲区满时放入数据,消费者也不会在缓冲区空时消耗数据。如果方法不够完善容易出现死锁的情况,此时进程都在等待唤醒。
实质上,很多后台服务程序并发控制的基本原理都可以归纳为生产者/消费者模式,解决生产者/消费者问题的方法可分为两类:
(1)采用某种机制保护生产者和消费者之间的同步;
(2)在生产者和消费者之间建立一个管道。
第一种方式有较高的效率,并且易于实现,代码的可控制性较好,属于常用的模式。第二种管道缓冲区不易控制,被传输数据对象不易于封装等,实用性不强。
同步问题核心在于:如何保证同一资源被多个线程并发访问时的完整性。常用的同步方法是采用信号或加锁机制,保证资源在任意时刻至多被一个线程访问。
(1)wait() / notify()方法
当缓冲区已满时,生产者线程停止执行,放弃锁,使自己处于等状态,让其他线程执行;
当缓冲区已空时,消费者线程停止执行,放弃锁,使自己处于等状态,让其他线程执行。
当生产者向缓冲区放入一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态;
当消费者从缓冲区取出一个产品时,向其他等待的线程发出可执行的通知,同时放弃锁,使自己处于等待状态。
(2)await() / signal()方法
(3)BlockingQueue阻塞队列方法
(4)信号量
(4)PipedInputStream / PipedOutputStream(管道)
代码实现:
仓库类
package 生产消费模型;
import java.util.LinkedList;
public class 仓库 {
private final int MAX_SIZE = 20;//仓库最大容量
private LinkedList<Object> list = new LinkedList<>();//仓库存储介质
public void produce(){
synchronized (list) {
while(list.size()==MAX_SIZE){
System.out.println("【生产者"+Thread.currentThread().getName()+"】仓库已满");
try {
list.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
list.add(new Object());
System.out.println("【生产者"+Thread.currentThread().getName()+"】生产一个产品,现库存"+list.size());
list.notify();
}
}
public void consume(){
synchronized (list){
while(list.size()==0){
System.out.println("【消费者"+Thread.currentThread().getName()+"】仓库为空");
try {
list.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
};
}
list.remove();
System.out.println("【消费者" + Thread.currentThread().getName()+ "】消费一个产品,现库存" + list.size());
list.notify();
}
}
}
生产者线程
package 生产消费模型;
public class 生产者 implements Runnable{
private 仓库 storage;
public 生产者(仓库 storage){
this.storage = storage;
}
public void run() {
while(true){
try {
storage.produce();
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
消费者线程
package 生产消费模型;
public class 消费者 implements Runnable{
private 仓库 storage;
public 消费者(仓库 storage){
this.storage = storage;
}
public void run(){
while(true){
try {
storage.consume();
Thread.sleep(3000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
主函数
package 生产消费模型;
public class Test {
public static void main(String[] args) {
仓库 storage = new 仓库();
Thread p1 = new Thread(new 生产者(storage));
Thread p2 = new Thread(new 生产者(storage));
Thread p3 = new Thread(new 生产者(storage));
Thread c1 = new Thread(new 消费者(storage));
Thread c2 = new Thread(new 消费者(storage));
Thread c3 = new Thread(new 消费者(storage));
p1.start();
p2.start();
p3.start();
c1.start();
c2.start();
c3.start();
}
}
剩余4种方法之后上线~