生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。通常采用进程间通信的方法解决该问题,常用的方法有信号灯法等。如果解决方法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。该问题也能被推广到多个生产者和消费者的情形。
package thread;
public class TestProducerConsumer {
public static void main(String[] args) {
Pool pool = new Pool(); //创建储物池
Producer producer = new Producer(pool); //创建生产者消费者对象
Consumer consumer = new Consumer(pool);
producer.start(); //开启线程
consumer.start();
}
}
class Producer extends Thread { //生产者线程
Pool pool = new Pool();
public Producer(Pool pool) {
this.pool = pool;
}
@Override
public void run() { //生产10个产品
for (int i = 0; i < 10; i++) {
pool.put(i);
}
}
}
class Consumer extends Thread { //消费者线程
Pool pool = new Pool();
public Consumer(Pool pool) {
this.pool = pool;
}
@Override
public void run() { //消费10个产品
for (int i = 0; i < 10; i++) {
pool.get();
}
}
}
class Pool{ //储物池类(包括生产和消费同步方法)
private int id;
private boolean flag = false; //标志生产者生产是否完成(消费者是否完成消费)
public synchronized void put(int id){ //生产同步方法,需放入生产出的产品id
while (flag){ //消费者没完成消费
try {
wait(); //当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//消费者完成消费(flag == false)释放锁后生产者开始生产
System.out.println("开始生产" + id);
try {
Thread.sleep((int)Math.random() * 100); //随机数模拟生产耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
this.id = id;
System.out.println("结束生产" + id);
flag = true; //设置生产完成标志
notify(); //唤醒等待队列中的线程(消费者)
}
public synchronized int get(){ //消费同步方法,返回消费产品id
while(!flag){ //生产者还没完成生产
try {
wait(); //当前线程等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//生产者完成生产后消费者消费
System.out.println("开始消费" + id);
try {
Thread.sleep((int)Math.random() * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("结束消费" + id);
flag = false; //提醒生产者生产
notify(); //唤醒生产者
return id;
}
}