生产者/消费者问题是个典型的多线程问题,类似于hello world对于一门编程语言而言,涉及的对象包括“生产者”、“消费者”、“仓库”和“产品”。
该模型需要注意以下几点:
1、生产者只有在仓库未满的时候生产,仓满则停止生产。
2、消费者只有在仓库有产品的情况下才能消费,空仓则等待。
3、当消费者发现没有产品时通知生产者生产。
4、生产者在生产出可消费的产品时,通知等待的消费者去消费。
此模型需要结合Object类的wait、notify、notifyAll方法。
package Progress.Thread; import java.lang.reflect.Constructor; import java.lang.reflect.Proxy; /** * @date 2018/5/4 16:07 * 消费者/生产者问题 */ public class Test { public static void main(String[] args) { Godown godown = new Godown(100); Producer pro = new Producer(godown); Consumer con = new Consumer(godown); pro.produce(60); pro.produce(120); con.consume(90); con.consume(150); con.consume(110); } } /**仓库*/ class Godown extends Thread{ /**仓库容量*/ private int capacity; /**仓库的实际容量*/ private int size; public Godown() { } public Godown(int capacity) { this.capacity = capacity; this.size = 0; } public synchronized void produce(int value){ //value表示想要生产的数量(有可能生产量太多需要多次生产) try { while (value>0){ //库存满的时候等待消费者消费产品 while (size>=capacity) { wait(); } //获取实际生产的数量(即库存中增加的数量) //如果 库存+想要生产的数量>总的容量,则 实际增量=总的容量-实际容量(此时仓库满仓) //否则 实际增量=想要生产的数量 int increment = (size+value)>capacity ? capacity - size : value; size = size + value; value -= increment; System.out.println(Thread.currentThread()+" "+getName()+" "+value+" "+size+" "+increment); //System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", //Thread.currentThread().getName(), increment, size,value); //通知消费者消费 notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } public synchronized void consume(int value){ try { //value表示要消费的数量(可能消费量太大数量不够,要多次提供以供消费) while (value>0){ //库存为0时,等待生产者生产 while (size<=0) { wait(); } //获取实际消费的数量,即库中实际减少的量 //如果 库存<要消耗的量,则 实际消耗量 = 库存 //否则,实际消耗的量 = 要消耗的量 int decent = size<value?size:value; size -= decent; value -= decent; System.out.println(Thread.currentThread()+" "+getName()+" "+value+" "+size+" "+decent); //System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", //Thread.currentThread().getName(), decent,value, size); //通知生产者生产 notifyAll(); } } catch (InterruptedException e) { e.printStackTrace(); } } @Override public String toString() { return "Godown{" + "capacity=" + capacity + ", size=" + size + '}'; } } /**生产者*/ class Producer extends Thread{ private Godown godown; public Producer(Godown godown) { this.godown = godown; } //新建一个线程向仓库中生产产品 public void produce(final int val){ new Thread(){ @Override public void run(){ godown.produce(val); } }.start(); } } /**消费者*/ class Consumer extends Thread{ private Godown godown; public Consumer(Godown godown) { this.godown = godown; } //新建一个线程去消耗产品 public void consume(final int val){ new Thread(){ @Override public void run(){ godown.consume(val); } }.start(); } }
生产者与仓库关联,当调用produce方法时,会生成一个线程向仓库中生产产品。
消费者与仓库关联,当调用consume方法时,会生成一个线程从仓库中获得商品用于消费。
produce方法和consume方法都是synchronized的意味着进入就会获得仓库对象的同步锁。也就是说同一时间,生产者和消费者线程只能有一个能够运行,另一个必须等待,通过同步锁实现了对“残酷”的互斥访问。
produce方法:当仓库满时,生产者线程等待,等待消费者消费商品之后,生产者才生产;生产之后通过notifyAll唤醒同步锁上的所有线程,包括消费者线程,“通知消费者消费”
consume方法:当空仓的时候,消费者等待,等待生产者生产商品之后,消费者才消费;消费者通过notifyAll唤醒同步锁上的所有线程,包括生产者线程,“通知生产者生产”