灵感来源于一个猪队友给我的题目

看到这个,我抓住的关键字是:任何子任务失败,要通知所有子任务执行取消逻辑。

这不就是消息广播吗?观察者模式!

干活

首先是收听者

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设计模式——观察者模式的灵活应用的更多相关文章

  1. java设计模式--观察者模式(Observer)

    java设计模式--观察者模式(Observer) java设计模式--观察者模式(Observer) 观察者模式的定义: 定义对象间的一种一对多的依赖关系.当一个对象的状态发生改变时,所有依赖于它的 ...

  2. 【设计模式】Java设计模式 - 观察者模式

    [设计模式]Java设计模式 - 观察者模式 不断学习才是王道 继续踏上学习之路,学之分享笔记 总有一天我也能像各位大佬一样 @一个有梦有戏的人 @怒放吧德德 分享学习心得,欢迎指正,大家一起学习成长 ...

  3. JAVA 设计模式 观察者模式

    用途 观察者模式 (Observer) 观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象. 这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己. 观 ...

  4. Java设计模式--观察者模式到监听器

    观察者模式是对象的行为模式.又叫做发布-订阅模式.模型-视图模式.源-监听器模式. 抽象主题角色:主题角色将所有对观察者对象的引用到保存在一个集合里,每个主题都可以拥有任意数量的观察者.抽象主题提供一 ...

  5. Java设计模式の观察者模式(推拉模型)

    目录: 一.观察者定义 二.观察者模式的结构(推模式实现) 三.推模型和拉模型(拉模式实现) 四.JAVA提供的对观察者模式的支持 五.使用JAVA对观察者模式的支持(自带推模式实现实例) 一.观察者 ...

  6. Java设计模式 - 观察者模式

    定义 观察者模式属于对象行为型模式. 在对象之间定义一对多的依赖,这样一来当一个对象改变状态,依赖它的对象都会收到通知并自动更新. 优点 1.  主题和观察者之间抽象耦合.无论什么对象主要实现了特定的 ...

  7. 我的Java设计模式-观察者模式

    相信大家都有看过<喜洋洋与灰太狼>,说的是灰太狼和羊族的"斗争",而每次的结果都是灰太狼一飞冲天,伴随着一句"我还会回来的......".为灰太狼感 ...

  8. java设计模式-观察者模式学习

    最近学习了设计模式中的观察者模式,在这里记录下学习成果. 观察者模式,个人理解:就是一个一对多模型,一个主体做了事情,其余多个主体都可以观察到.只不过这个主体可以决定谁去观察他,以及做什么事情可以给别 ...

  9. Java设计模式——观察者模式(事件监听)

    最近在看Tomcat和Spring的源码,在启动的时候注册了各种Listener,事件触发的时候就执行,这里就用到了设计模式中的观察者模式. 引-GUI中的事件监听 想想以前在学Java的GUI编程的 ...

随机推荐

  1. DEM轨迹后处理

    更推荐的方法:https://www.cnblogs.com/Jay-CFD/p/12195294.html 2020.6.16更新 方法一:直接在paraview中显示 首先在输出颗粒信息的时候保存 ...

  2. STM32入门系列-GPIO概念介绍

    GPIO(general purpose intput output)是通用输入输出端口的简称,可以通过软件来控制其输入和输出.STM32 芯片的 GPIO 引脚与外部设备连接起来,从而实现与外部通讯 ...

  3. JMeter实战(三) 界面使用

    JMeter 有 2 种运行方式,一种是 CLI,一种是 GUI,本篇文章就来介绍一下后者,图形用户界面,因为后续文章大部分都是基于 GUI 的. 本文演示的是英文版,想用中文的同学可以在菜单栏点击 ...

  4. python数据类型之set(集合)

    set集合 关注公众号"轻松学编程"了解更多. 1.概述 set与dict类似,但set是一组key的集合,与dict的区别在于set不存储value. 本质:无序且无重复元素的集 ...

  5. linux修改ssh远程端口22

    建议先查看redhat的release版本,CentOS 7的启动服务不同: # more /etc/redhat-release 正文: 如题: 在此前,建议先查看redhat的release版本, ...

  6. Netty源码解析 -- ChannelPipeline机制与读写过程

    本文继续阅读Netty源码,解析ChannelPipeline事件传播原理,以及Netty读写过程. 源码分析基于Netty 4.1 ChannelPipeline Netty中的ChannelPip ...

  7. 分布式文档存储数据库之MongoDB索引管理

    前文我们聊到了MongoDB的简介.安装和对collection的CRUD操作,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13941797.html:今天我 ...

  8. 双十一,就用turtle画个单身狗送给自己

    今年的双十一到了 但还有谁记得双十一是 单身狗的节日 单身狗的我是时候站出来 捍卫自己的权益了 单身是一种怎样的状态? 我们所有人都单身过,但也许只有很少的人真正体验过. 短视频内容完全是假的,全程是 ...

  9. ashx将datatable返回json数据

    1.直接使用JsonConvert.SerializeObject().将datatable放入  输出字符串 下面是测试:用webform+ashx作为接口. public class GetJso ...

  10. Elasticsearch(6):文档查询

      为方便后续查询演示,我们先创建一个索引.创建索引请求如下: