现在编码的时候,为了处理消息,大家动不动就上个重器,例如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. 3种方式自动化控制APP

    自动化控制APP不管是在工作还是生活方面,都可以帮助我们高效地完成任务,节省时间和精力.本文主要介绍自动化控制APP的3种常用方式. 1.Python + adb 这种方式需要对Android有一些基 ...

  2. RT-Thread线程同步与线程通信

    一.线程同步 线程同步的使用场景 例如一项工作中的两个线程:一个线程从传感器中接收数据并且将数据写到共享内存中,同时另一个线程周期性的从共享内存中读取数据并发送去显示,下图描述了两个线程间的数据传递: ...

  3. 四:海思Hi3516CV500/Hi3516DV300

    Hi3516CV500 和 Hi3516DV300 均是海思推出的 IP Camera  SoC [System-on-a-Chip:SoC芯片是一种集成电路的芯片] 芯片. 针对海思 HI3516D ...

  4. 利用PostMan 模拟上传/下载文件

    我们经常用postman模拟各种http请求.但是有时候因为业务需要,我们需要测试上传下载功能.其实postman也是很好支持这两种操作的. 一.上传文件: 1.打开postman 选择对应reque ...

  5. VMware最小化安装Centos7.6-无桌面

    目录 安装包工具 新建虚拟机 安装 centos 7.6 系统 终端登陆系统 设置ip地址 关闭防火墙 关闭 SELINUX SELINUX=enforcing 硬盘挂载 桥接上网方式 安装包工具 V ...

  6. 微信小程序关于小说类使用官方阅读器

    https://doc.weixin.qq.com/doc/w3_AAcAYAbdAFwpM63n1R5SIat3aa4cX?scode=AJEAIQdfAAoYHVCBbdAG4A1QYmAFQ 上 ...

  7. docker之企业级镜像仓库Harbor

    Harbor概述 Habor是由VMWare公司开源的容器镜像仓库.事实上,Habor是在Docker Registry上进行了相应的 企业级扩展,从而获得了更加广泛的应用,这些新的企业级特性包括:管 ...

  8. pod(四):pod的重启策略和生命周期

    目录 一.系统环境 二.前言 三.pod的重启策略 四.pod的生命周期 一.系统环境 服务器版本 docker软件版本 Kubernetes(k8s)集群版本 CPU架构 CentOS Linux ...

  9. C# 炸弹人 winform版

    实现这个游戏的基本功能包含几个对象:玩家,怪物,墙砖,炸弹,通关的门.玩家通过上下左右方向键移动,放置炸弹,被怪物杀死,被炸弹炸死.怪物随机方向移动,能杀死玩家.炸弹有爆炸功能,炸弹的火花长度.通过的 ...

  10. django多表关联实战

    定义模型类: from django.db import models from django.contrib.auth.models import User ''' ---------- Djang ...