现在编码的时候,为了处理消息,大家动不动就上个重器,例如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. [PHP] Laravel 依赖注入使用不当引起的内存溢出

    业务逻辑: 正常在 controller 方法的参数中注入某个类,方法中使用这个类时发生内存超出提示. 分析: 过往显示,正常使用依赖注入是不存在问题的,那么很有可能是哪里发生了循环引用,导致一直请求 ...

  2. dotnet 6 使用 DependentHandle 关联对象生命周期

    本文将告诉大家在 dotnet 6 新加入的 System.Runtime.DependentHandle 的类型的使用方法,通过 DependentHandle 可以实现将某个对象的引用生命周期和另 ...

  3. van-tab吸顶后头部透明色渐变响应

    方法一:监听滚动事件 $('.scrollContent').bind('touchmove', function(e){             var  winHeight = $(window) ...

  4. 【转载】只有.dbf数据文件进行数据库恢复

    此篇文章为转载,来自 " ITPUB博客 " ,链接:http://blog.itpub.net/26015009/viewspace-714742/ 个人mark下,在之后dbf ...

  5. 【简说Python WEB】Jinja2模板

    目录 [简说Python WEB]Jinja2模板 目前环境的代码树 抽离出来的Html模板 渲染模板 条件语句 循环语句 系统环境:Ubuntu 18.04.1 LTS Python使用的是虚拟环境 ...

  6. Phpstrom开发工具Sftp的使用

  7. Go语言的包(package)

    包名是从$GOPATH/src/后开始计算的,使用/进行路径分隔. 想要被别的包调用标识符都要的首字母大. 单行导入和多行导入. 导入包不想使用内部的标识符,需要使用匿名导入. 每个包导入的时候会自动 ...

  8. USB3.0与Type-C接口的关系

    USB全称为Universal Serial Bus,翻译过来就是通用串行总线,是连接计算机与外部设备的一种串口总线标准.USB的发展经历了一下阶段: USB1.0:1.5Mbps(192KB/s)低 ...

  9. python教程6.6-发送邮件smtplib

    实现步骤: Python对SMTP⽀持有 smtplib 和 email 两个模块, email 负责构造邮件, smtplib 负责发送邮件,它对smtp协议进⾏了简单的封装. 简单代码示例: 发送 ...

  10. Selenium4自动化测试3--元素定位By.NAME,By.LINK_TEXT 和通过链接部分文本定位,By.PARTIAL_LINK_TEXT,css_selector定位,By.CSS_SELECTOR

    4-通过名称定位,By.NAME name属性为表单中客户端提交数据的标识,一个网页中name值可能不是唯一的.所以要根据实际情况进行判断 import time from selenium impo ...