Java中的设计模式(二):生产者-消费者模式与观察者模式

简介: 做一个简单的延伸和对比

Java中的设计模式(二):生产者-消费者模式与观察者模式.png

人生苦短,不如养狗

一、前言

  在上一篇 Java中的设计模式(一):观察者模式 中我们了解了 观察者模式 的基本原理和使用场景,在今天的这篇文章中我们要做一点简单的延伸性学习——对比一下 生产者-消费者模式观察者模式的异同。

二、什么是“生产者-消费者模式”?

  和观察者模式不同,生产者-消费者模式 本身并不属于设计模式中的任何一种 。那么生产者-消费者模式到底是什么呢?下面我们用一个例子简单说明一下:

生产者-消费者模式.jpg

  如同上图中所示,生产者和消费者就如同一本杂志的投稿作者和订阅的读者,同一本杂志的投稿作者可以有多个,它的读者也可以有多个,而杂志就是连接作者和读者的桥梁(即缓冲区)。通过杂志这个数据缓冲区,作者可以将完成的作品投递给订阅了杂志的读者,在这一过程中,作者不用关心读者是否收到了作品或是否完成了阅读,作者和读者是两个相对独立的对象,两者的行为互不影响。

  可以看到,在这个例子当中出现了三个角色,分别是 生产者消费者 以及 缓冲区 。生产者和消费者比较好理解,前者是生产数据,后者则是处理前者生产出来的数据。而缓冲区在生产者-消费者模式中则起到了一个 解耦支持异步支持忙闲不均 的作用。

三、两者的区别

1. 编程范式不同

  生产者-消费者模式和观察者模式的第一个不同点在上面已经说过,前者是一种 面向过程 的软件设计模式,不属于Gang of Four提出的23种设计模式中的任何一种,而后者则是23中设计模式中的一种,也即面向对象的设计模式中的一种。

2. 关联关系不同

  这一理念上的不同就带出了下一种不同点,即观察者模式中只有一对多的关系,没有多对多的关系,而在生产者-消费者模式中则是多对多的关系。

  在观察者模式中,被观察者只有一个,观察者却可以有多个。就比如十字路口的交通灯,直行的车辆只会观察控制直行的交通灯,不会去观察控制左拐或者右拐的交通灯,也就是说观察的对象是固定唯一的。

  而在生产者-消费者模式中则不同,生产者可以有多个,消费者也可以有多个。还是用上面作者和读者的例子,在这个例子当中,读者只关心杂志的内容而不必关心内容的创作者是谁,作者也只需要知道创作完的作品可以发布到对应的杂志,而不必关心会有那些读者。

3. 耦合关系不同

  从上一个不同中不难看出生产者-消费者模式和观察者模式的耦合关系也不相同,前者为 轻耦合 ,后者为 重耦合

4. 应用场景不同

  观察者模式多用于 事件驱动模型 当中,生产者-消费者模式则多出现在 进程间通信 ,用于进行解耦和并发处理,我们常用的消息队列用的就是生产者-消费者模式。当然在Java中使用生产者-消费者模式还需要注意缓冲区的线程安全问题,这里就不做过多叙述。

四、一个小例子

  最后用一个简单的demo来结束本次的延伸学习。

1. StoreQueue--缓冲区

publicclassStoreQueue<T> {
privatefinalBlockingQueue<T>queue=newLinkedBlockingQueue<>();
/*** 队列中增加数据** @param data 生产者生产的数据*/publicvoidadd(Tdata) {
try {
queue.put(data);
        } catch (Exceptione) {
e.printStackTrace();
        }
    }
/*** 队列中获取数据** @return 从队列中获取到的数据*/publicTget() {
try {
returnqueue.take();
        } catch (Exceptione) {
e.printStackTrace();
        }
returnnull;
    }
}

  在这个例子中,我们使用了jdk自身的 阻塞队列BlockingQueue 来实现了一个缓冲区,这里只需要实现放数据和取数据的方法。如果我们自己实现一个阻塞队列,一方面需要注意阻塞的处理,另一方面需要考虑线程安全的问题,这里就不展开叙述了,有兴趣的同学可以看下BlockingQueue的源码。

2. Producer--生产者

publicclassProducerimplementsRunnable{
privateStoreQueue<String>storeQueue;
publicProducer(StoreQueue<String>storeQueue) {
this.storeQueue=storeQueue;
    }
@Overridepublicvoidrun() {
for (inti=0; i<10; i++) {
storeQueue.add(Thread.currentThread().getName() +":"+i);
        }
    }
}

3. Consumer--消费者

publicclassConsumerimplementsRunnable{
privateStoreQueue<String>storeQueue;
publicConsumer(StoreQueue<String>storeQueue) {
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) {
StoreQueue<String>storeQueue=newStoreQueue<>();
Producerproducer=newProducer(storeQueue);
Consumerconsumer=newConsumer(storeQueue);
ProducerproducerTwo=newProducer(storeQueue);
ConsumerconsumerTwo=newConsumer(storeQueue);
newThread(producer).start();
newThread(consumer).start();
newThread(producerTwo).start();
newThread(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:5
当前消费线程 : 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:9
当前消费线程 : Thread-1, 接收到数据 : Thread-0:6

  可以看到在上面的数据结果中,不同生产者生产的数据只会被一个消费者消费,没有出现线程安全问题,这要归功于实现缓冲区使用到的 BlockingQueue

相关文章
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
|
1月前
|
设计模式 开发者 Python
Python编程中的设计模式:工厂方法模式###
本文深入浅出地探讨了Python编程中的一种重要设计模式——工厂方法模式。通过具体案例和代码示例,我们将了解工厂方法模式的定义、应用场景、实现步骤以及其优势与潜在缺点。无论你是Python新手还是有经验的开发者,都能从本文中获得关于如何在实际项目中有效应用工厂方法模式的启发。 ###
|
19天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
29天前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
33 4
|
1月前
|
设计模式 安全 Java
Kotlin - 改良设计模式 - 构建者模式
Kotlin - 改良设计模式 - 构建者模式
|
1月前
|
设计模式 安全 Java
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
Kotlin教程笔记(51) - 改良设计模式 - 构建者模式
41 1
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
消息中间件 Java Kafka
Java实现Kafka生产者和消费者的示例
Kafka是由Apache软件基金会开发的一个开源流处理平台,由Scala和Java编写。Kafka的目标是为处理实时数据提供一个统一、高吞吐、低延迟的平台。
413 0
Java实现Kafka生产者和消费者的示例
|
消息中间件 Java Kafka
Java实现Kafka生产者与消费者
Java实现Kafka生产者与消费者
714 0
|
4天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
35 6