问题描述:
生产者消费者问题(Producer-consumer problem)是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。同样,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。
如何模拟这个问题?
为了模拟这个问题,我们假设有一个篮子,这个篮子用于装刚刚生产出来的面包,这个篮子能够容纳的面包个数是固定的,所以,如果生产者(面包师傅)发现篮子已经满了,那么就返回厨房等待,当消费者拿掉了一个面包后,消费者会通知厨房的面包师傅,这个篮子已经不是满的了,可以再往里面加面包了。
同理,如果消费者发现篮子里没有面包了,那么他也只能在厅堂等着,当面包师傅往篮子里加了面包以后,面包师傅会通知那些在厅堂里等候拿面包的人,篮子里面已经有面包了。
Bread类用于模拟面包
public class Bread { int id; public Bread(int id) { this.id = id; } public String toString() { return "Bread: " + id; } }
public class BreadBucket { int index = 0; Bread[] container; public BreadBucket(int size) { container = new Bread[size]; } public synchronized void putBread(Bread bread) { while (index == container.length) { try { this.wait(); // The bucket is full, and the producer needs to wait } catch (InterruptedException e) { e.printStackTrace(); } } container[index] = bread; index++; System.out.println("+++++++New bread added: " + bread); System.out.println("+++++++Current # of bread in the bucket:" + index); this.notify(); // notify consumer, the bucket is not empty. } public synchronized void takeBread() { while (index == 0) { try { this.wait(); // The consumer needs to wait because the bucket is empty. } catch (InterruptedException e) { e.printStackTrace(); } } index--; Bread bread = container[index]; System.out.println("-------Consumed bread: " + bread); System.out.println("-------Current # of bread in the bucket:" + index); this.notify(); // notify producer, the bucket is not full } }
public class Consumer implements Runnable { BreadBucket container; public Consumer(BreadBucket container) { this.container = container; } @Override public void run() { while (true) { container.takeBread(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public class Producer implements Runnable { BreadBucket container; int id = 1; public Producer(BreadBucket container) { this.container = container; } @Override public void run() { while (true) { Bread bread = new Bread(id++); container.putBread(bread); try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } } } }
public class Test { public static void main(String[] args) { BreadBucket container = new BreadBucket(10); Producer p = new Producer(container); Consumer c = new Consumer(container); new Thread(p).start(); new Thread(c).start(); } }