现在编码的时候,为了处理消息,大家动不动就上个重器,例如MQ之类的。但很多时候,并不是那么有必要,因为数据量和并发其实远远不够。

可以替代的方案非常多,其中一个是java.util.concurrent。

在jdk9及其以上,java.util.Observable已经被标注为过时,官方推荐使用java.beans或者是java.util.concurrent。

在发布订阅者模式中,有四个对象是需要关注的:

  1. 发布者
  2. 订阅者(消费者)
  3. 消息
  4. 并发

本文主要代码参考 https://www.cnblogs.com/zhangmingda/p/14715139.html

不过为了更加友好一些,对原有的代码做了适量的调整。

注:以下代码运行于windows11+JDK17

消息对象

package study.base.designPattern.observer.latest.flow;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; public class Message {
private Integer qty;
private Integer maxQty;
private String content;
private Map<String, Boolean> readStatus; public Map<String, Boolean> getReadStatus() {
return readStatus;
} public String getContent() {
return content;
} public boolean isFinished() {
return qty.intValue() == maxQty.intValue();
} public Message(String content, List<String> readerList) {
this.content = content;
this.maxQty = readerList.size();
this.qty = 0;
this.readStatus = new ConcurrentHashMap<>();
for (String item : readerList) {
readStatus.put(item, false);
}
} public void read(String readerName) {
if (qty < maxQty) {
if (readStatus.containsKey(readerName)) {
Boolean isRead = readStatus.get(readerName);
if (!isRead) {
synchronized (readStatus) {
readStatus.put(readerName, true);
qty++;
System.out.println("---|"+readerName + "正在进行" + this.content + "...." + qty);
if (qty==maxQty) {
System.out.println("\n---|所有人已经完成【"+this.content+"】\n");
}
}
}
}
} }
}

消费者对象

package study.base.designPattern.observer.latest.flow;

import java.util.concurrent.Flow.Subscriber;
import java.util.concurrent.Flow.Subscription; public class Student implements Subscriber<Message> { private Subscription subscription;
private String name; public Student(String name) {
this.name = name;
} @Override
public void onSubscribe(Subscription subscription) {
System.out.println(name + "开始订阅[" + subscription.toString() + "]");
subscription.request(1);
this.subscription = subscription;
} @Override
public void onError(Throwable e) {
System.out.println(e.getMessage());
} @Override
public void onComplete() {
System.out.println(name + "关闭了订阅");
Teacher.getLock().lock();
Teacher.getCondition().signalAll();
Teacher.getLock().unlock(); } @Override
public void onNext(Message message) {
message.read(this.name);
subscription.request(1);
/**
* 模拟延时--郊游的时候,时间长一些
*/
try {
if (message.getContent().equals("郊游")) {
Thread.sleep(1300);
}
else {
Thread.sleep(200);
} } catch (InterruptedException e) {
e.printStackTrace();
} } }

发布者和主程序

package study.base.designPattern.observer.latest.flow;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.SubmissionPublisher;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.serializer.SerializerFeature; /**
* https://www.cnblogs.com/zhangmingda/p/14715139.html 此类技术是观察者模式的一个并发实现
*
* @author lzfto
*/
public class Teacher {
private static Lock lock = new ReentrantLock(true);
private static Condition condition = lock.newCondition(); private volatile static CopyOnWriteArrayList<Message> subjectList = new CopyOnWriteArrayList<Message>(); public static Lock getLock() {
return lock;
} public static Condition getCondition() {
return condition;
} public static void main(String[] args) throws InterruptedException { /**
* 定义一个发布者,需要设定要发送消息的泛型数据类型
*/
SubmissionPublisher<Message> teacher = new SubmissionPublisher<>();
/**
* 定义一个订阅者
*/
List<String> studentList = Arrays.asList("mzd", "***", "luzhfiei", "海瑞");
for (String readerName : studentList) {
Student student = new Student(readerName);
teacher.subscribe(student);
}
/**
* 测试发布消息
*/ subjectList.add(new Message("朗读", studentList));
subjectList.add(new Message("劳动", studentList));
subjectList.add(new Message("郊游", studentList));
subjectList.add(new Message("射箭", studentList)); subjectList.forEach(item -> {
System.out.println("同学们!开始【" + item.getContent() + "】");
teacher.submit(item);
}); // 向订阅者发布数据,需要保持主线程存活,否则当前线程执行结束,发布者和订阅者都被销毁了。
/**
* 关闭消息发布
*/
teacher.close(); // 关闭后,如果当前线程未退出,待订阅者所有消息都处理完毕才会运行订阅者的onComplete方法
lock.lock();
condition.await();
lock.unlock();
System.out.println(JSONArray.toJSONString(subjectList, SerializerFeature.PrettyFormat));
;
} }

测试后的输出(一种情况):

mzd开始订阅[java.util.concurrent.SubmissionPublisher$BufferedSubscription@12749d9e]
***开始订阅[java.util.concurrent.SubmissionPublisher$BufferedSubscription@1df086d4]
luzhfiei开始订阅[java.util.concurrent.SubmissionPublisher$BufferedSubscription@1e7629c2]
海瑞开始订阅[java.util.concurrent.SubmissionPublisher$BufferedSubscription@73e441ec]
同学们!开始【朗读】
同学们!开始【劳动】
同学们!开始【郊游】
同学们!开始【射箭】
---|mzd正在进行朗读....1
---|海瑞正在进行朗读....2
---|luzhfiei正在进行朗读....3
---|***正在进行朗读....4 ---|所有人已经完成【朗读】 ---|海瑞正在进行劳动....1
---|luzhfiei正在进行劳动....2
---|***正在进行劳动....3
---|mzd正在进行劳动....4 ---|所有人已经完成【劳动】 ---|***正在进行郊游....1
---|mzd正在进行郊游....2
---|luzhfiei正在进行郊游....3
---|海瑞正在进行郊游....4 ---|所有人已经完成【郊游】 ---|mzd正在进行射箭....1
---|***正在进行射箭....2
---|luzhfiei正在进行射箭....3
---|海瑞正在进行射箭....4 ---|所有人已经完成【射箭】 luzhfiei关闭了订阅
***关闭了订阅
mzd关闭了订阅
海瑞关闭了订阅

subjectList的结果:

[
{
"content": "朗读",
"finished": true,
"readStatus": {
"mzd": true,
"海瑞": true,
"***": true,
"luzhfiei": true
}
},
{
"content": "劳动",
"finished": true,
"readStatus": {
"mzd": true,
"海瑞": true,
"***": true,
"luzhfiei": true
}
},
{
"content": "郊游",
"finished": true,
"readStatus": {
"mzd": true,
"海瑞": true,
"***": true,
"luzhfiei": true
}
},
{
"content": "射箭",
"finished": true,
"readStatus": {
"mzd": true,
"海瑞": true,
"***": true,
"luzhfiei": true
}
}
]

这只是一个非常简单的例子,距离生产还有很远的一个距离。

java并发的发布和订阅测试的更多相关文章

  1. 转MQTT--Python进行发布、订阅测试

    前言  使用python编写程序进行测试MQTT的发布和订阅功能.首先要安装:pip install paho-mqtt 测试发布(pub)  我的MQTT部署在阿里云的服务器上面,所以我在本机上编写 ...

  2. Java并发--安全发布对象

    单例模式 懒汉模式:多线程非线程安全,在多线程中,可能会产生多个对象 饿汉模式:线程安全. 类加载的时候初始化,不推荐在构造函数需要做耗时操作的时候使用,因为可能导致类加载缓慢,而且可能初始化后并没有 ...

  3. MQTT介绍(3)java模拟MQTT的发布,订阅

    MQTT目录: MQTT简单介绍 window安装MQTT服务器和client java模拟MQTT的发布,订阅 在此强调一下mqtt的使用场景: 1.不可靠.网络带宽小的网络 2.运行的设备CPU. ...

  4. Java实现Redis的消息订阅和发布

    1.  首先需要一个消息监听器类 package com.sogou.baike.testimport.testSubscribe; import redis.clients.jedis.JedisP ...

  5. java 多线程 发布订阅模式:发布者java.util.concurrent.SubmissionPublisher;订阅者java.util.concurrent.Flow.Subscriber

    1,什么是发布订阅模式? 在软件架构中,发布订阅是一种消息范式,消息的发送者(称为发布者)不会将消息直接发送给特定的接收者(称为订阅者).而是将发布的消息分为不同的类别,无需了解哪些订阅者(如果有的话 ...

  6. Java高并发--安全发布对象

    Java高并发--安全发布对象 主要是学习慕课网实战视频<Java并发编程入门与高并发面试>的笔记 发布对像:使一个对象能够被当前范围之外的对象使用. 对象逸出:一种错误的发布.当一个对象 ...

  7. windows环境下apache-apollo服务器搭建及发布订阅测试

    查证了一些资料之后,发现 apache-apollo服务器使用的人还是挺多的,资料也比较齐全,所以直接选择 apache-apollo了,具体性能如何,先用起来再说吧: 1.下载 apache-apo ...

  8. 《java并发编程实战》读书笔记2--对象的共享,可见性,安全发布,线程封闭,不变性

    这章的主要内容是:如何共享和发布对象,从而使它们能够安全地由多个线程同时访问. 内存的可见性 确保当一个线程修改了对象状态后,其他线程能够看到发生的状态变化. 上面的程序中NoVisibility可能 ...

  9. Java并发编程(五):Java线程安全性中的对象发布和逸出

    发布(Publish)和逸出(Escape)这两个概念倒是第一次听说,不过它在实际当中却十分常见,这和Java并发编程的线程安全性就很大的关系. 什么是发布?简单来说就是提供一个对象的引用给作用域之外 ...

  10. java并发编程笔记(四)——安全发布对象

    java并发编程笔记(四)--安全发布对象 发布对象 使一个对象能够被当前范围之外的代码所使用 对象逸出 一种错误的发布.当一个对象还没构造完成时,就使它被其他线程所见 不安全的发布对象 某一个类的构 ...

随机推荐

  1. dotnet win32 使用 WIC 获取系统编解码器

    在 Windows 系统上,有一个很重要的概念是 Windows Imaging Component 也就是 WIC 层,这是专门用来处理多媒体相关的系统组件,特别是用来处理图片相关,包括编码和解码和 ...

  2. Ubuntu 通过本机代理修复 NuGet 还原 error NU1301 失败

    在国内垃圾的网络环境下,我在虚拟机里面安装了 Ubuntu 系统,准备用来测试 MAUI 在 Linux 上的行为,然而使用 dotnet restore 构建时,提示 NU1301 失败.我通过配置 ...

  3. 2018-2-13-win10-uwp-从StorageFile获取文件大小

    title author date CreateTime categories win10 uwp 从StorageFile获取文件大小 lindexi 2018-2-13 17:23:3 +0800 ...

  4. 2018-7-15-WPF-在-DrawingContext-的-push-如何使用

    title author date CreateTime categories WPF 在 DrawingContext 的 push 如何使用 lindexi 2018-7-15 15:51:0 + ...

  5. nginx+uwsgi介绍

    一.nginx+uwsgi介绍 pip list # 查看安装过的模块 rpm -q nginx # 查看是否安装某款服务 pip install django == 1.11.11 # 安装djan ...

  6. 机器学习策略篇:详解开发集和测试集的大小(Size of dev and test sets)

    在深度学习时代,设立开发集和测试集的方针也在变化. 可能听说过一条经验法则,在机器学习中,把取得的全部数据用70/30比例分成训练集和测试集.或者如果必须设立训练集.开发集和测试集,会这么分60%训练 ...

  7. 基于改进MFCC特征和卷积递归神经网络的心音分类

    具体的软硬件实现点击http://mcu-ai.com/MCU-AI技术网页_MCU-AI人工智能 心音分类在心血管疾病的早期发现中起着至关重要的作用,特别是对于小型初级卫生保健诊所.尽管近年来心音分 ...

  8. ansible(5)--ansible的script模块

    1. script模块 作用:在远程主机运行本地的脚本: 调用格式: -m script -a "/PATH/TO/SCRIPT_FILE": 参数: creates:如果其后跟的 ...

  9. linux打包压缩工具详解

    linux打包压缩工具详解 目录 linux打包压缩工具详解 1.linux文件压缩工具 1.1 compress命令详解 1.2 gzip命令详解 1.3 bzip2命令详解 1.4 xz命令详解 ...

  10. es请求方式调用

    Es基础 关系: ElasticSearch-> mysql index (索引)-> 数据库 Documents(文档) -> row(行) Fileds(字段)-> col ...