现在编码的时候,为了处理消息,大家动不动就上个重器,例如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. vue-单独引入css文件,设置公共的css样式或者修改默认的vant,mint样式

    1.css文件夹下新建global.css文件并粘贴复制: ======================= :root{     --bgColor : #d3252a;     --pinkColo ...

  2. Mybatis的逆向工程(generator)

    Tips:Mybatis generator官网 http://www.mybatis.org/generator/configreference/commentGenerator.html Myba ...

  3. Radius 现在是云原生计算基金会(CNCF)的沙箱项目

    在数字化时代,云原生计算技术逐渐成为企业转型的关键.2024-04-25,备受瞩目的开源项目 Radius 已正式加入云原生计算基金会(CNCF)的沙箱项目![Sandbox] Radius · Is ...

  4. 一种利用光电容积描记(PPG)信号和深度学习模型对高血压分类的新方法

    具体的软硬件实现点击 http://mcu-ai.com/ MCU-AI技术网页_MCU-AI 据世界心脏联合会统计,截至 2022 年,全球有 13 亿人被诊断患有高血压,每年约有 1000 万人死 ...

  5. vue3编译优化之“静态提升”

    前言 在上一篇 vue3早已具备抛弃虚拟DOM的能力了文章中讲了对于动态节点,vue做的优化是将这些动态节点收集起来,然后当响应式变量修改后进行靶向更新.那么vue对静态节点有没有做什么优化呢?答案是 ...

  6. liunx下redis的哨兵环境搭建

    哨兵简介 一定要有一个概念:哨兵实例也是特殊的Redis实例,也就是哨兵实例是独立的进程,多个哨兵实例可以搭建主从(Master-Slave),它们承担的职责和普通的Redis实例不一样.下面是官方文 ...

  7. WampServer 的安装

    一, 下载   wampserver3.2.0_x64.exe  文件 二,在D盘新建wamp64文件 三,以管理员的方式运行安装文件 只有两种语方,选择 English 接受协议 下一步: 点击下一 ...

  8. Android OpenMAX(二)OMX Component实现基础

    Android OpenMAX IL提供的API位于 frameworks/native/headers/media_plugin/media/openmax ,目录下存放有预定义的类型.组件句柄定义 ...

  9. nginx+php,nginx+tomcat动静分离实战

    1. 动静分离实战 1.1.1 nginx+tomcat 动静分离 主机 用途 10.0.0.63 tomcat服务器 10.0.0.64 nginx服务器 1.1.2 安装 java+tomcat环 ...

  10. css之伪元素选择器

    注:本博客内容来自尚硅谷禹神的前端入门课程 什么是伪元素? 很像元素,但不是元素(element),是元素中的一些特殊位置. 伪元素语法中的::可以用:,因为css2中没有明确区分伪类和伪元素,但是s ...