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

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

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

干活

首先是收听者

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. 使用Vue简单的写组件的UI库

    初始化项目vue create nature-ui 在根目录下面创建一个文件目录放置组件(我这里的创建packages) packages 目录下面创建个个组件的名称并创建index.js(用于输出所 ...

  2. java实现单链表、栈、队列三种数据结构

    一.单链表 1.在我们数据结构中,单链表非常重要.它里面的数据元素是以结点为单位,每个结点是由数据元素的数据和下一个结点的地址组成,在java集合框架里面 LinkedList.HashMap(数组加 ...

  3. requestS模块发送请求的时候怎么传递参数

    首先要确定接口的传递参数是什么类型的,如果接口是查询,使用get请求方法,传递参数的时候使用params, 如果接口需要的json型参数的话,使用json,如果是上传文件的话,通过files参数在传递 ...

  4. golang1.16内嵌静态资源指南

    今天是万圣节,也是golang1.16新特性冻结的日子.不得不说自从go2路线发布之后golang新特性的迭代速度也飞速提升,1.16中有相当多的重要更新,包括io标准库的重构,语言内置的静态资源嵌入 ...

  5. 【Jmeter】设置语言为中文

    1.临时设置 Jmeter菜单栏选择Options-Choose Language-Chinese (Simplified) 这种方法,重启软件后又变为英文环境了. 2.永久设置 进入apache-j ...

  6. SpringBoot中的classpath

    一句话总结:classpath 等价于 main/java + main/resources + 第三方jar包的根目录.下面详细解释. 首先,classpath顾名思义,是编译之后项目的路径,而不是 ...

  7. C#实现的几种委托方式介绍

    //普通委托            DeleteShow ds = new DeleteShow(ShowName);            Console.WriteLine("----- ...

  8. C# out参数的学习

    out参数一直不是很会用,学习下记录下来 . 先来看一下out参数的使用场景 如果你在一个方法中 ,返回多个相同类型的值的时候,可以考虑返回一个数组.(举例:写一个方法,传入一个数组,返回数组的最大值 ...

  9. canvas基础[二]教你编写贝塞尔曲线工具

    贝塞尔曲线 bezierCurveTo 在线工具 https://canvature.appspot.com/ [感觉这个好用一些] https://blogs.sitepointstatic.com ...

  10. 面试题:你有没有搞混查询缓存和Buffer Pool

    一. 关注送书!<Netty实战> 文章公号号首发!连载中!关注微信公号回复:"抽奖" 可参加抽活动 首发地址:点击跳转阅读原文,有更好的阅读体验 使用推荐阅读,有更好 ...