JAVA多线程学习之生产者消费者模式(三)
生产者消费者模式是并发、多线程编程中经典的设计模式,生产者和消费者通过分离的执行工作解耦,简化了开发模式。生产者和消费者模式在生活当中随处可见,它描述的是协调与协作的关系。比如我去包子铺买包子,店铺老板属于生产者,而我属于消费者。而包子铺的蒸笼属于我与店铺老板的媒介。店铺老板生产包子,把它放到蒸笼上去蒸,包子熟了,我从蒸笼里去拿包子。如果蒸笼里包子放满了,店铺老板就停止做包子,等待包子销售出去;如果蒸笼的包子都卖完了,则我停止拿包子,等待店铺老板做包子。
对于这种情况,我们有三种方式来实现。
第一种,wait()和notify()实现线程通信。
/** * * @author saule * @date 2019年3月26日 下午6:30:54 * @Description 包子容器,即蒸笼 */ public class Bread { private int count=0; public synchronized void increase(){ //如果蒸笼里包子等于6个,则等待,不生产 while(count==6){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count++; System.out.println("生产包子成功!包子数量为"+count); this.notifyAll(); } public synchronized void decrease(){ //如果蒸笼包子没了,则等待,不再消费包子 while(count==0){ try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } count--; System.out.println("消费包子成功!包子数量为"+count); this.notifyAll(); } }
/** * * @author saule * @date 2019年3月26日 下午7:12:27 * @Description 生产者 */ public class Producer implements Runnable { private Bread bread; public Producer(Bread bread) { this.bread = bread; } @Override public void run() { while(true){ try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } bread.increase(); } } }
/** * * @author saule * @date 2019年3月26日 下午7:12:14 * @Description 消费者 */ public class Consumer implements Runnable { private Bread bread; public Consumer(Bread bread){ this.bread=bread; } @Override public void run() { while(true){ try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } bread.decrease(); } } }
public class BreadDemo { public static void main(String[] args) { Bread bread=new Bread(); Producer producer=new Producer(bread); Consumer consumer=new Consumer(bread); Thread t1=new Thread(producer); Thread t2=new Thread(consumer); t1.start(); t2.start(); } } 测试结果: 生产包子成功!包子数量为1 生产包子成功!包子数量为2 生产包子成功!包子数量为3 生产包子成功!包子数量为4 生产包子成功!包子数量为5 生产包子成功!包子数量为6 消费包子成功!包子数量为5 生产包子成功!包子数量为6 消费包子成功!包子数量为5 生产包子成功!包子数量为6 消费包子成功!包子数量为5
第二种,采用阻塞队列实现
阻塞队列实现生产者消费者模式超级简单,它提供开箱即用支持阻塞的方法put()和take(),开发者不需要写困惑的wait-nofity代码去实现通信。BlockingQueue 一个接口,Java5提供了不同的现实,如ArrayBlockingQueue和LinkedBlockingQueue,两者都是先进先出(FIFO)顺序。而ArrayLinkedQueue是自然有界的,LinkedBlockingQueue可选的边界。下面这是一个完整的生产者消费者代码例子,对比传统的wait、nofity代码,它更易于理解。
/** * * @author saule * @date 2019年3月26日 下午7:44:56 * @Description 包子容器 */ public class Bread { private BlockingQueue queue=new LinkedBlockingDeque(10); //向蒸笼放包子 public void increase(){ try { queue.put(1); System.out.println("店铺老板做了个包子,目前蒸笼里有"+queue.size()+"个包子。"); } catch (InterruptedException e) { e.printStackTrace(); } } //从蒸笼拿包子 public void decrease(String name){ try { queue.take(); System.out.println(name+"吃了个包子,目前蒸笼里还有"+queue.size()+"个包子。"); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * * @author saule * @date 2019年3月26日 下午7:55:31 * @Description 生产者 */ public class ProducerQueue implements Runnable { private Bread bread; public ProducerQueue(Bread bread) { this.bread = bread; } @Override public void run() { while(true){ try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } bread.increase(); } } } /** * * @author saule * @date 2019年3月26日 下午8:05:34 * @Description 消费者 */ public class ConsumerQueue implements Runnable { private Bread bread; public ConsumerQueue(Bread bread) { this.bread = bread; } @Override public void run() { while(true){ try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } bread.decrease(Thread.currentThread().getName()); } } } public class BreadDemo { public static void main(String[] args) { Bread bread=new Bread(); ProducerQueue pq=new ProducerQueue(bread); ConsumerQueue cq=new ConsumerQueue(bread); Thread p=new Thread(pq); Thread c1=new Thread(cq,"顾客一"); Thread c2=new Thread(cq,"顾客二"); Thread c3=new Thread(cq,"顾客三"); p.start(); c1.start(); c2.start(); c3.start(); } } 测试结果:
店铺老板做了个包子,目前蒸笼里有1个包子。
店铺老板做了个包子,目前蒸笼里有2个包子。
店铺老板做了个包子,目前蒸笼里有3个包子。
店铺老板做了个包子,目前蒸笼里有4个包子。
店铺老板做了个包子,目前蒸笼里有5个包子。
顾客一吃了个包子,目前蒸笼里还有4个包子。
顾客三吃了个包子,目前蒸笼里还有3个包子。
顾客二吃了个包子,目前蒸笼里还有2个包子。
店铺老板做了个包子,目前蒸笼里有3个包子。
店铺老板做了个包子,目前蒸笼里有4个包子。
店铺老板做了个包子,目前蒸笼里有5个包子。
店铺老板做了个包子,目前蒸笼里有6个包子。
店铺老板做了个包子,目前蒸笼里有7个包子。
店铺老板做了个包子,目前蒸笼里有8个包子。
顾客一吃了个包子,目前蒸笼里还有7个包子。
第三种,采用lock和condition实现
/** * * @author saule * @date 2019年3月26日 下午11:53:53 * @Description 公共资源 */ public class Resource { //当前资源数量 private int num=0; //最大资源数量 private int size=10; private Lock lock; private Condition producerCondition; private Condition consumerCondition; public Resource(Lock lock, Condition producerCondition,Condition consumerCondition) { this.lock = lock; this.producerCondition = producerCondition; this.consumerCondition = consumerCondition; } /** * 向资源池添加资源 */ public void increase(){ lock.lock(); try{ if(num<size){ num++; System.out.println(Thread.currentThread().getName()+"生产了一件资源,当前资源池有"+num+"个"); //唤醒消费者 consumerCondition.signalAll(); }else{ //让生产线程等待 try { producerCondition.await(); System.out.println("waiting1..."); } catch (InterruptedException e) { e.printStackTrace(); } } }finally{ lock.unlock(); } } /** * 从资源池中取走资源 */ public void decrease(){ lock.lock(); try{ if(num>0){ num--; System.out.println(Thread.currentThread().getName()+"消费了一件资源,当前资源池有"+num+"个"); //唤醒生产者 producerCondition.signalAll(); }else{ //让消费线程等待 try { consumerCondition.await(); System.out.println("waiting2..."); } catch (InterruptedException e) { e.printStackTrace(); } } }finally{ lock.unlock(); } } } /** * * @author saule * @date 2019年3月27日 上午12:10:58 * @Description 生产者 */ public class ProducerThread implements Runnable { private Resource resource; public ProducerThread(Resource resource) { this.resource = resource; } @Override public void run() { while(true){ try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); } resource.increase(); } } } /** * * @author saule * @date 2019年3月27日 上午12:21:52 * @Description 消费者 */ public class ConsumerThread implements Runnable { private Resource resource; public ConsumerThread(Resource resource) { this.resource = resource; } @Override public void run() { while(true){ try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } resource.decrease(); } } } public class LockCondition { public static void main(String[] args) { Lock lock=new ReentrantLock(); Condition producerCondition=lock.newCondition(); Condition consumerCondition=lock.newCondition(); Resource resource=new Resource(lock, producerCondition, consumerCondition); ProducerThread pt=new ProducerThread(resource); ConsumerThread ct=new ConsumerThread(resource); Thread tp=new Thread(pt, "生产者"); Thread ct1=new Thread(ct, "消费者1"); Thread ct2=new Thread(ct, "消费者2"); Thread ct3=new Thread(ct, "消费者3"); tp.start(); ct1.start(); ct2.start(); ct3.start(); } } 测试结果: 生产者生产了一件资源,当前资源池有1个 生产者生产了一件资源,当前资源池有2个 生产者生产了一件资源,当前资源池有3个 生产者生产了一件资源,当前资源池有4个 生产者生产了一件资源,当前资源池有5个 生产者生产了一件资源,当前资源池有6个 生产者生产了一件资源,当前资源池有7个 生产者生产了一件资源,当前资源池有8个 生产者生产了一件资源,当前资源池有9个 消费者1消费了一件资源,当前资源池有8个 消费者3消费了一件资源,当前资源池有7个 消费者2消费了一件资源,当前资源池有6个 生产者生产了一件资源,当前资源池有7个 生产者生产了一件资源,当前资源池有8个 生产者生产了一件资源,当前资源池有9个 生产者生产了一件资源,当前资源池有10个 消费者1消费了一件资源,当前资源池有9个 消费者3消费了一件资源,当前资源池有8个 消费者2消费了一件资源,当前资源池有7个

浙公网安备 33010602011771号