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

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

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

干活

首先是收听者

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. [String] intern()方法

    intern()方法设计的初衷,就是重用String对象,以节省内存消耗. JDK1.6以及以前版本中,常量池是放在 Perm 区(属于方法区)中的,熟悉JVM的话应该知道这是和堆区完全分开的. 使用 ...

  2. JUC---01阻塞队列(BlockingQueue)

    一.什么是阻塞队列 阻塞队列是一个队列,在数据结构中起的作用如上图:当队列是空的,从队列中获取元素的操作将会被阻塞:当队列是满的,从队列中添加元素的操作将会被阻塞 1.为什么需要BlockingQue ...

  3. 怎么提高selenium脚本的自动化执行效率?

    1.使用配置更高的电脑,选择更快的网络环境:2.使用效率更高的语言,比如 java 比 python,这种方案可行性不高:3.能直接访问网址的,就不要一步一步点击:4.不要盲目增加 sleep,尽量使 ...

  4. (一)http协议介绍

    HTTP协议详解 (一) 介绍 HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本 ...

  5. .netcore实现jwt身份验证

    前言 http协议本身是一种无状态的协议.所以客户端的每次请求,服务端是不清楚其身份的,需要客户端每次都要将身份信息传入,服务进行验证,才能达到安全验证的目的. 传统的Web用户验证:1.客户端传入用 ...

  6. Go之发送钉钉和邮箱

    smtp发送邮件 群发两个邮箱,一个163,一个QQ package main import ( "fmt" "net/smtp" "strings& ...

  7. 【Kata Daily 190916】String end with?(字母结尾)

    题目: Complete the solution so that it returns true if the first argument(string) passed in ends with ...

  8. Docker系列01—Docker 基础入门

    一.初识Docker和容器 1.1 什么是docker 容纳其他物品的工具,可以部分或完全封闭,被用于容纳.存储.运输物品.物体可以被放置在容器中,而容器则可以保护内容物. 容器? 容器就是在隔离的环 ...

  9. SU+GIS,让SketchUp模型在地图上活起来

    一.SU+GIS的场景展示 skp与卫星地图和倾斜摄影模型相结合人工模型与实景模型完美融合 这么一看是不是直接秒杀了单纯看看skp后联想的规划效果? 二.如何快速把草图大师的结果和GIS结合呢?在图新 ...

  10. ubuntu下minicom安装和简单设置使用

    minicom是一个串口通信工具. Ubuntu下的安装: 打开终端 sudo apt-get install minicom即可完成安装. 简单设置使用: 安装完成后第一次启动时运行 sudo mi ...