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

相关文章
|
11月前
|
设计模式 缓存 Java
Java设计模式(二):观察者模式与装饰器模式
本文深入讲解观察者模式与装饰器模式的核心概念及实现方式,涵盖从基础理论到实战应用的全面内容。观察者模式实现对象间松耦合通信,适用于事件通知机制;装饰器模式通过组合方式动态扩展对象功能,避免子类爆炸。文章通过Java示例展示两者在GUI、IO流、Web中间件等场景的应用,并提供常见陷阱与面试高频问题解析,助你写出灵活、可维护的代码。
|
设计模式 负载均衡 监控
并发设计模式实战系列(2):领导者/追随者模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第二章领导者/追随者(Leader/Followers)模式,废话不多说直接开始~
397 0
|
设计模式 监控 Java
并发设计模式实战系列(1):半同步/半异步模式
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发设计模式实战系列,第一章半同步/半异步(Half-Sync/Half-Async)模式,废话不多说直接开始~
565 0
|
9月前
|
设计模式 消息中间件 传感器
Java 设计模式之观察者模式:构建松耦合的事件响应系统
观察者模式是Java中常用的行为型设计模式,用于构建松耦合的事件响应系统。当一个对象状态改变时,所有依赖它的观察者将自动收到通知并更新。该模式通过抽象耦合实现发布-订阅机制,广泛应用于GUI事件处理、消息通知、数据监控等场景,具有良好的可扩展性和维护性。
656 8
|
Java 应用服务中间件 Docker
java-web部署模式概述
本文总结了现代 Web 开发中 Spring Boot HTTP 接口服务的常见部署模式,包括 Servlet 与 Reactive 模型、内置与外置容器、物理机 / 容器 / 云环境部署及单体与微服务架构,帮助开发者根据实际场景选择合适的方案。
701 25
|
设计模式 算法 Java
设计模式觉醒系列(04)策略模式|简单工厂模式的升级版
本文介绍了简单工厂模式与策略模式的概念及其融合实践。简单工厂模式用于对象创建,通过隐藏实现细节简化代码;策略模式关注行为封装与切换,支持动态替换算法,增强灵活性。两者结合形成“策略工厂”,既简化对象创建又保持低耦合。文章通过支付案例演示了模式的应用,并强调实际开发中应根据需求选择合适的设计模式,避免生搬硬套。最后推荐了JVM调优、并发编程等技术专题,助力开发者提升技能。
|
12月前
|
存储 Java 大数据
Java 大视界 -- Java 大数据在智能家居能源消耗模式分析与节能策略制定中的应用(198)
简介:本文探讨Java大数据技术在智能家居能源消耗分析与节能策略中的应用。通过数据采集、存储与智能分析,构建能耗模型,挖掘用电模式,制定设备调度策略,实现节能目标。结合实际案例,展示Java大数据在智能家居节能中的关键作用。
|
设计模式 缓存 安全
Java设计模式的单例模式应用场景
Java设计模式的单例模式应用场景
453 4
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
282 4
|
设计模式 存储 负载均衡
【五】设计模式~~~创建型模式~~~单例模式(Java)
文章详细介绍了单例模式(Singleton Pattern),这是一种确保一个类只有一个实例,并提供全局访问点的设计模式。文中通过Windows任务管理器的例子阐述了单例模式的动机,解释了如何通过私有构造函数、静态私有成员变量和公有静态方法实现单例模式。接着,通过负载均衡器的案例展示了单例模式的应用,并讨论了单例模式的优点、缺点以及适用场景。最后,文章还探讨了饿汉式和懒汉式单例的实现方式及其比较。
【五】设计模式~~~创建型模式~~~单例模式(Java)