工具与资源中心帮助开发者更高效地工作,提供围绕开发者全生命周期的工具与资源https://developer.aliyun.com/...1.前言上一篇Java中的设计模式(一):观察者模式我们已经了解了观察者模式的基本原理和使用场景。在今天的文章中,我们将做一点扩展学习——比较生产者消费者模型和观察者模型的异同。2、什么是“生产者消费者模型”?与观察者模式不同的是,生产者消费者模式本身不属于任何一种设计模式。那么究竟什么是生产者消费者模式呢?我们举个例子简单说明一下:如上图所示,生产者和消费者就像一本杂志的投稿人和读者。同一本杂志的撰稿人可以有多个,其读者也可以有多个,杂志是连接作者和读者的桥梁(即缓冲区)。通过杂志的数据缓冲区,作者可以将完成的作品交付给订阅该杂志的读者。在此过程中,作者无需关心读者是否收到作品或完成阅读。作者和读者是两个相对独立的客体,两者的行为互不影响。可以看到,这个例子中一共有三个角色,分别是Producer、Consumer和Buffer。生产者和消费者比较好理解,前者是生产数据,后者是对前者生产的数据进行处理。在producer-consumer模式下,buffer起到了解耦,支持异步,支持忙闲不均的作用。3.两者的区别1.编程范式不同第一个生产者消费者模型和观察者模型的区别上面已经说了。前者是面向过程的软件设计模型,不是四人帮提出的。23种设计模式中的任意一种,后者是23种设计模式之一,即面向对象设计模式之一。2.关联不同这种概念上的差异带来了下一个差异,即在观察者模式下只有一对多关系,而在生产者消费者模式下没有多对多关系。这是一个多对多的关系。在观察者模式下,只有一个观察者,但可以有多个观察者。就像路口的红绿灯一样,直行车辆只会观察控制直行的红绿灯,而不会观察控制左转或右转的红绿灯。也就是说,观察的对象是固定的、唯一的。在生产者-消费者模式下,则不同,可以有多个生产者,也可以有多个消费者。还是用上面作者和读者的例子。在这个例子中,读者只关心杂志的内容,不需要关心内容的创作者是谁。有那些读者。3.耦合关系不同?从前面的不同不难看出,生产者-消费者模式和观察者模式的耦合关系也是不同的。前者为轻耦合,后者为重耦合。4.应用场景不同?观察者模式多用于事件驱动模型,而生产者消费者模式多用于进程间通信,用于解耦和并发处理。常用的消息队列是生产者消费者模式。当然在Java中使用生产者消费者模式也需要注意缓冲区的线程安全,这里不再赘述。4.一个小例子最后,用一个简单的demo来结束本次拓展学习。1.StoreQueue--bufferpublicclassStoreQueue{privatefinalBlockingQueuequeue=newLinkedBlockingQueue<>();/***向队列中添加数据**@paramdata生产者生产的数据*/publicvoidadd(Tdata){try{queue.put(data);}catch(Exceptione){e.printStackTrace();}}/***从队列中获取数据**@return从队列中获取数据*/publicTget(){try{returnqueue.take();}catch(Exceptione){e.printStackTrace();}返回空值;}}本例中我们使用jdk自带的阻塞队列BlockingQueue来实现一个buffer,这里只需要实现放数据和取数据的方法即可。如果我们自己实现一个阻塞队列,一方面需要注意阻塞的处理,另一方面需要考虑线程安全的问题,这里就不赘述了。有兴趣的同学可以看看BlockingQueue的源码。2.Producer--producerpublicclassProducerimplementsRunnable{privateStoreQueuestoreQueue;publicProducer(StoreQueuestoreQueue){this.storeQueue=storeQueue;}@Overridepublicvoidrun(){for(inti=0;i<10;i++){storeQueue.add(Thread.currentThread().getName()+":"+i);}}}3。Consumer——消费者公共类ConsumerimplementsRunnable{privateStoreQueuestoreQueue;publicConsumer(StoreQueuestoreQueue){this.storeQueue=storeQueue;}@Overridepublicvoidrun(){try{while(true){Stringdata=storeQueue.get();System.out.println("当前消费线程:"+Thread.currentThread().getName()+",接收到的数据:"+data);}}catch(Exceptione){e.printStackTrace();Thread.currentThread().interrupt();}}}4。执行逻辑和执行结果执行逻辑publicstaticvoidmain(String[]args){StoreQueuestoreQueue=newStoreQueue<>();生产者producer=newProducer(storeQueue);消费者consumer=newConsumer(storeQueue);生产者producerTwo=newProducer(storeQueue);消费者consumerTwo=newConsumer(storeQueue);新线程(生产者).start();新线程(消费者).start();新线程(producerTwo).start();新线程(consumerTwo).start();}运行结果当前消耗线程:Thread-1,接收数据:Thread-0:0当前消耗线程:Thread-1,接收数据:Thread-0:1当前消耗线程:Thread-1,接收数据:Thread-0:2当前消耗线程:Thread-1,接收数据:Thread-0:3当前消耗线程:Thread-1,接收数据:Thread-0:4当前消耗线程:Thread-3,接收数据:Thread-0:5Current消费线程:Thread-3,接收数据:Thread-0:7当前消费线程:Thread-3,接收数据:Thread-0:8当前消费线程:Thread-3,接收数据:Thread-0:9当前消费线程:Thread-3,接收数据:Thread-2:0当前消耗线程:Thread-3,接收数据:Thread-2:1当前消耗线程:Thread-3,接收数据:Thread-2:2当前消耗线程:Thread-3、接收数据:Thread-2:3当前消耗线程:Thread-3,接收数据:Thread-2:4当前消耗线程:Thread-3,接收数据:Thread-2:5当前消耗线程:Thread-3,接收数据:Thread-2:6当前消耗线程:Thread-3,接收数据:Thread-2:7当前消耗线程:Thread-3,接收数据:Thread-2:8当前消耗线程:Thread-3,接收数据:Thread-2:9Currentconsumerthread:Thread-1,Receiveddata:Thread-0:6从上面的数据结果可以看出,不同生产者生产的数据只会被一个消费者消费,不会出现线程安全问题,感谢用于实现buffer的BlockingQueue本文转自:https://developer.aliyun.com/...