靈感來源於一個豬隊友給我的題目
看到這個,我抓住的關鍵字是:任何子任務失敗,要通知所有子任務執行取消邏輯。
這不就是訊息廣播嗎?觀察者模式!
幹活
首先是收聽者
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(); } }
這個是這麼多年第一個發到首頁的,就是想問下大家怎樣解決這種情況下的執行緒停止問題