Java设计模式——观察者模式的灵活应用
灵感来源于一个猪队友给我的题目

看到这个,我抓住的关键字是:任何子任务失败,要通知所有子任务执行取消逻辑。
这不就是消息广播吗?观察者模式!
干活
首先是收听者
package com.example.broadcast; /**
* 每个节点即是广播者,也是收听者
*/
public interface Listener { /**
* 设置调度中心
*/
void setCenter(DispatchCenter center); /**
* 主动通知其它收听者
*/
void notice(String msg); /**
* 自己收到通知的处理逻辑
* @param msg
*/
void whenReceived(String msg); /**
* 收听者标志:唯一
* @return
*/
String identify(); }
然后是调度中心
package com.example.broadcast; /**
* 调度中心
*/
public interface DispatchCenter { /**
* 广播
* @param own 广播的时候,要排除自己
* @param msg 广播消息
*/
void broadcast(String own, String msg); /**
* 添加收听者
* @param listener
*/
void addListener(Listener listener); }
调度中心实现
package com.example.broadcast; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; public class DispatchCenterImpl implements DispatchCenter { private static final Map<String, Listener> MAP = new ConcurrentHashMap<>(); @Override
public void broadcast(String own, String msg) {
MAP.forEach((k,v) -> {
// 不用给自己发通知
if (!k.equals(own)){
v.whenReceived(msg);
}
});
} @Override
public void addListener(Listener listener) {
listener.setCenter(this);
MAP.put(listener.identify(), listener);
}
}
剩下三个收听者
package com.example.broadcast;
import java.util.UUID;
public class ListenerA implements Listener {
private DispatchCenter center;
private String identify;
public ListenerA() {
identify = UUID.randomUUID().toString();
}
@Override
public void setCenter(DispatchCenter center) {
this.center = center;
}
@Override
public void notice(String msg) {
center.broadcast(identify, msg);
}
@Override
public void whenReceived(String msg) {
System.out.println(this.getClass().getName() + "收到消息:" + msg);
}
@Override
public String identify() {
return identify;
}
}
B和C除了类名不一样,其他都一样,不再赘述。目录如下

测试
package com.example.broadcast; import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Main { public static void main(String[] args) {
DispatchCenter center = new DispatchCenterImpl();
ListenerA listenerA = new ListenerA();
ListenerB listenerB = new ListenerB();
ListenerC listenerC = new ListenerC();
center.addListener(listenerA);
center.addListener(listenerB);
center.addListener(listenerC); ExecutorService executorService = Executors.newFixedThreadPool(3); // A触发1条事件
executorService.submit(() -> {
int i = 1;
while (i > 0){
listenerA.notice(listenerA.getClass().getName() + "说:我有" + new Random().nextInt(1000000) + "元");
i--;
}
});
// B触发2条事件
executorService.submit(() -> {
int i = 2;
while (i > 0){
listenerB.notice(listenerB.getClass().getName() + "说:我有" + new Random().nextInt(1000000) + "元");
i--;
}
});
// C触发3条事件
executorService.submit(() -> {
int i = 3;
while (i > 0){
listenerC.notice(listenerC.getClass().getName() + "说:我有" + new Random().nextInt(1000000) + "元");
i--;
}
}); executorService.shutdown(); } }
输出:

流程图

当其中的B节点,发生了错误,除了把自己处理好之外
1. 向调度中心发送广播请求,并携带需要的消息
2. 调度中心遍历收听者,挨个通知(执行)每一个收听者接受消息的逻辑
关于停止任务
因为题目要求,【快速取消】所有子任务
关于线程停止的方法也有很多:
1. 优雅退出run方法
2. 暴力stop
3. run方法抛出异常
如果说要求,A异常了,B和C收到消息之后,线程立即停止,不能有一点迟疑,说实话我还没想到该怎么做。因为你要知道,实际上的任务的run方法内部,不太可能是个while循环,人家可能就是个顺序执行,所以停止标志位的方式,并不适用。
而其它的方法,我也没想到很好的。我只能写个按照标志位停止的“玩具”
修改三个收听者代码和测试类
package com.example.broadcast; import lombok.SneakyThrows; import java.util.Random;
import java.util.UUID; public class ListenerA implements Listener,Runnable { private DispatchCenter center;
private String identify; public ListenerA() {
identify = UUID.randomUUID().toString();
} @Override
public void setCenter(DispatchCenter center) {
this.center = center;
} @Override
public void notice(String msg) {
center.broadcast(identify, msg);
} @Override
public void whenReceived(String msg) {
System.out.println(this.getClass().getName() + "收到消息:" + msg);
} @Override
public String identify() {
return identify;
} @SneakyThrows
@Override
public void run() {
// 5秒之后,模拟发生异常
Thread.sleep(5000);
notice(this.getClass().getName() + "说:我有" + new Random().nextInt(1000000) + "元");
System.out.println(this.getClass().getName() + "程序异常,并已经传播了消息...");
}
}
package com.example.broadcast;
import lombok.SneakyThrows;
import java.util.UUID;
public class ListenerB implements Listener,Runnable {
private DispatchCenter center;
private String identify;
private volatile Boolean stopFlag = false;
public ListenerB() {
identify = UUID.randomUUID().toString();
}
@Override
public void setCenter(DispatchCenter center) {
this.center = center;
}
@Override
public void notice(String msg) {
center.broadcast(identify, msg);
}
@Override
public void whenReceived(String msg) {
System.out.println(this.getClass().getName() + "_" + Thread.currentThread().getName() + "收到消息:" + msg);
// 停止当前线程
stopFlag = true;
}
@Override
public String identify() {
return identify;
}
@SneakyThrows
@Override
public void run() {
while (!stopFlag){
Thread.sleep(1000);
System.out.println(this.getClass().getName() + "_" + Thread.currentThread().getName() + "__B在执行任务");
}
System.out.println(this.getClass().getName() + "_" + Thread.currentThread().getName() + "__B Dead");
}
}
package com.example.broadcast;
import lombok.SneakyThrows;
import java.util.UUID;
public class ListenerC implements Listener,Runnable {
private DispatchCenter center;
private String identify;
private volatile Boolean stopFlag = false;
public ListenerC() {
identify = UUID.randomUUID().toString();
}
@Override
public void setCenter(DispatchCenter center) {
this.center = center;
}
@Override
public void notice(String msg) {
center.broadcast(identify, msg);
}
@Override
public void whenReceived(String msg) {
System.out.println(this.getClass().getName() + "_" + Thread.currentThread().getName() + "收到消息:" + msg);
// 停止当前线程
stopFlag = true;
}
@Override
public String identify() {
return identify;
}
@SneakyThrows
@Override
public void run() {
while (!stopFlag){
Thread.sleep(1000);
System.out.println(this.getClass().getName() + "_" + Thread.currentThread().getName() + "__C在执行任务");
}
System.out.println(this.getClass().getName() + "_" + Thread.currentThread().getName() + "__C Dead");
}
}
测试
package com.example.broadcast; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class Main { public static void main(String[] args) {
DispatchCenter center = new DispatchCenterImpl();
ListenerA listenerA = new ListenerA();
ListenerB listenerB = new ListenerB();
ListenerC listenerC = new ListenerC();
center.addListener(listenerA);
center.addListener(listenerB);
center.addListener(listenerC); ExecutorService executorService = Executors.newFixedThreadPool(3); // A
executorService.submit(listenerA);
// B
executorService.submit(listenerB);
// C
executorService.submit(listenerC); executorService.shutdown(); } }

这个是这么多年第一个发到首页的,就是想问下大家怎样解决这种情况下的线程停止问题
Java设计模式——观察者模式的灵活应用的更多相关文章
- java设计模式--观察者模式(Observer)
java设计模式--观察者模式(Observer) java设计模式--观察者模式(Observer) 观察者模式的定义: 定义对象间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖于它的 ...
- 【设计模式】Java设计模式 - 观察者模式
[设计模式]Java设计模式 - 观察者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 @一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长 ...
- JAVA 设计模式 观察者模式
用途 观察者模式 (Observer) 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 观 ...
- Java设计模式--观察者模式到监听器
观察者模式是对象的行为模式.又叫做发布-订阅模式.模型-视图模式.源-监听器模式. 抽象主题角色:主题角色将所有对观察者对象的引用到保存在一个集合里,每个主题都可以拥有任意数量的观察者.抽象主题提供一 ...
- Java设计模式の观察者模式(推拉模型)
目录: 一.观察者定义 二.观察者模式的结构(推模式实现) 三.推模型和拉模型(拉模式实现) 四.JAVA提供的对观察者模式的支持 五.使用JAVA对观察者模式的支持(自带推模式实现实例) 一.观察者 ...
- Java设计模式 - 观察者模式
定义 观察者模式属于对象行为型模式. 在对象之间定义一对多的依赖,这样一来当一个对象改变状态,依赖它的对象都会收到通知并自动更新. 优点 1. 主题和观察者之间抽象耦合.无论什么对象主要实现了特定的 ...
- 我的Java设计模式-观察者模式
相信大家都有看过<喜洋洋与灰太狼>,说的是灰太狼和羊族的"斗争",而每次的结果都是灰太狼一飞冲天,伴随着一句"我还会回来的......".为灰太狼感 ...
- java设计模式-观察者模式学习
最近学习了设计模式中的观察者模式,在这里记录下学习成果. 观察者模式,个人理解:就是一个一对多模型,一个主体做了事情,其余多个主体都可以观察到.只不过这个主体可以决定谁去观察他,以及做什么事情可以给别 ...
- Java设计模式——观察者模式(事件监听)
最近在看Tomcat和Spring的源码,在启动的时候注册了各种Listener,事件触发的时候就执行,这里就用到了设计模式中的观察者模式. 引-GUI中的事件监听 想想以前在学Java的GUI编程的 ...
随机推荐
- Spring Boot注解与资源文件配置
date: 2018-11-18 16:57:17 updated: 2018-11-18 16:57:17 1.不需要多余的配置文件信息 application.properties mybatis ...
- nio DirectByteBuffer如何回收堆外内存
概述 使用了nio框架的应用,比如服务框架,利用nio建立长连接通信,他们会使用DirectByteBuffer来分配堆外内存,也就是本地直接内存,这个内存的回收不由gc直接维护,我们通常所说的gc, ...
- APP后台架构开发实践笔记
1 App后台入门 1.1 App后台的功能 (1)远程存储数据: (2)消息中转. 1.2 App后台架构 架构设计的流程 (1) 根据App的设计,梳理出App的业务流程: (2) 把每个业务流程 ...
- ABAP-字符串常用处理方法
字符串处理 SPLIT dobj AT sep INTO { {result1 result2 ...} | {TABLE result_tab} } 必须指定足够目标字段.否则,用字段dobj的剩余 ...
- requestS模块发送请求的时候怎么传递参数
首先要确定接口的传递参数是什么类型的,如果接口是查询,使用get请求方法,传递参数的时候使用params, 如果接口需要的json型参数的话,使用json,如果是上传文件的话,通过files参数在传递 ...
- php 计算2点之间的距离
//获取该点周围的4个点 $distance = 1;//范围(单位千米) $lat = 113.873643; $lng = 22.573969; define('EARTH_RADIUS', 63 ...
- 【Flutter 实战】pubspec.yaml 配置文件详解
老孟导读:pubspec.yaml 文件是 Flutter 中非常重要的配置文件,下面就让我们看看里面各个配置的含义. pubspec.yaml 是 Flutter 项目的配置文件,类似于 Andro ...
- .netcore基于mysql的codefirst
.netcore基于mysql的codefirst 此文仅是对于netcore基于mysql的简单的codefirst实现的简单记录.示例为客服系统消息模板的增删改查实现 第一步.创建实体项目,并在其 ...
- Java注解(入门级)
Java注解 前言 近日在阅读开源项目,发现项目里好多奇奇怪怪的注解(@DataScope.@Log...)看得我一脸懵,不知道大家是否也有过这样的经历,回想了一下,发现自己对于注解的知识,好像只停留 ...
- 手把手教你使用容器服务 TKE 集群审计排查问题
概述 有时候,集群资源莫名被删除或修改,有可能是人为误操作,也有可能是某个应用的 bug 或恶意程序调用 apiserver 接口导致,需要找出 "真凶".这时候,我们需要为集群开 ...