摘要:在前面的文章中介紹過觀察者模式及并發編程的基礎知識,為了讓大家更好的了解觀察者模式故而特意寫了這篇番外概述在多線程下我們需要知道當前執行線程的狀態是什么比如運行,關閉,異常等狀態的通知,而且不僅僅是更新當前頁面。
在前面的文章中介紹過 觀察者模式 及 并發編程的基礎知識,為了讓大家更好的了解觀察者模式故而特意寫了這篇番外..概述
在Java多線程下,我們需要知道當前執行線程的狀態是什么比如運行,關閉,異常等狀態的通知,而且不僅僅是更新當前頁面。
觀察者模式: 是一種使用率極高的模式,用于建立一種對象與對象之間的依賴關系,一個對象發生改變時將自動通知其他對象,其他對象將相應作出反應。在觀察者模式中,發生改變的對象稱為觀察目標,而被通知的對象稱為觀察者,一個觀察目標可以對應多個觀察者,而且這些觀察者之間可以沒有任何相互聯系,可以根據需要增加和刪除觀察者,使得系統更易于擴展。
觀察者模式傳送門:http://blog.battcn.com/2017/12/11/java/design-pattern/observer-pattern/#more
案例假設開發一個多線程爬蟲功能,由于數據過大需要利用多線程并行化來提升抓取的效率,并且在抓取過程中要記錄執行線程的運行狀態以便追溯問題原因
UML圖如下
1.定義具體觀察對象,實現JDK自帶的Observer接口,然后在需要實現的update方法中記錄下每個線程執行的狀態信息
class ObserverListener implements Observer { /** * 避免多線程鎖競爭 */ private static final Object LOCK = new Object(); @Override public void update(Observable observable, Object runnableEvent) { synchronized (LOCK) { ObservableRunnable.RunnableEvent event = (ObservableRunnable.RunnableEvent) runnableEvent; if (event != null) { if (event.getCause() != null) { System.out.println("The Runnable [" + event.getThread().getName() + "] process failed and state is " + event.getState().name()); event.getCause().printStackTrace(); } else { System.out.println("The Runnable [" + event.getThread().getName() + "] data changed and state is " + event.getState().name()); } } } } }
2.定義具體被觀察的對象,該對象需要繼承Observable類,以及實現Runnable接口,這里run的實現非常簡單,執行每一步驟操作時都進行了通知,通知觀察者消息發生變更了
為什么每次都需要 setChanged 呢
篩選有效通知,只有有效通知可以調用setChanged。比如,我的微信朋友圈一條狀態,好友A點贊,后續該狀態的點贊和評論并不是每條都通知A,只有A的好友觸發的操作才會通知A。
便于撤銷通知操作,在主題中,我們可以設置很多次setChanged,但是在最后由于某種原因需要取消通知,我們可以使用clearChanged輕松解決問題。
主動權控制,由于setChanged為protected,而notifyObservers方法為public,這就導致存在外部隨意調用notifyObservers的可能,但是外部無法調用setChanged,因此真正的控制權應該在主題這里。
class ObservableRunnable extends Observable implements Runnable { /** * 線程名稱 */ private String name; ObservableRunnable(String name, ObserverListener listener) { this.name = name; // 將被觀察的對象注冊到觀察者中 super.addObserver(listener); } /** * 發送通知 * * @param event 通知的內容 */ private void notifyChange(final RunnableEvent event) { // 前面說過 JDK自帶的 需要每次設置一次狀態,代表當前內容更改了 super.setChanged(); super.notifyObservers(event); } @Override public void run() { try { notifyChange(new RunnableEvent(RunnableState.RUNNING, Thread.currentThread(), null)); System.out.printf("根據 [%s] 查詢 ", this.name); Thread.sleep(1000L); if (this.name.equals("T3")) { // 故意模擬報錯 throw new RuntimeException("故意拋出錯誤"); } notifyChange(new RunnableEvent(RunnableState.DOWN, Thread.currentThread(), null)); } catch (Exception e) { notifyChange(new RunnableEvent(RunnableState.ERROR, Thread.currentThread(), e)); } } enum RunnableState { /** * RUNNING:運行 * ERROR:異常 * DOWN:正常結束 */ RUNNING, ERROR, DOWN } static class RunnableEvent { private final RunnableState state; private final Thread thread; private final Throwable cause; RunnableEvent(RunnableState state, Thread thread, Throwable cause) { this.state = state; this.thread = thread; this.cause = cause; } RunnableState getState() { return state; } public Thread getThread() { return thread; } Throwable getCause() { return cause; } } }
3.創建測試工程
public class ObserverClient { public static void main(String[] args) { ObserverListener listener = new ObserverListener(); Listnames = Arrays.asList("T1", "T2", "T3"); for (String name : names) { Thread thread = new Thread(new ObservableRunnable(name, listener)); thread.start(); } } }
4.運行結果,通過運行日志可以發現,啟動三個線程后同時執行抓取操作,但是Thread-2線程在數據處理時發生了異常,在ObserverListener處也成功收到通知的內容,然后對信息進行了輸出操作。在實際過程中我們可以為異常進行補償操作
The Runnable [Thread-1] data changed and state is RUNNING The Runnable [Thread-0] data changed and state is RUNNING 根據 [T1] 查詢 The Runnable [Thread-2] data changed and state is RUNNING 根據 [T2] 查詢 根據 [T3] 查詢 java.lang.RuntimeException: 故意拋出錯誤 The Runnable [Thread-0] data changed and state is DOWN at com.battcn.chapter14.ObservableRunnable.run(ObserverClient.java:67) The Runnable [Thread-1] data changed and state is DOWN at java.lang.Thread.run(Thread.java:745) The Runnable [Thread-2] process failed and state is ERROR總結
本文,簡單講述了多線程環境下如何利用觀察者模式進行線程狀態監聽,也是對前面所講的基礎進行鞏固,在學習的過程中,既要知其然也要知其所以然。這樣才能更好地駕馭它,更好地去理解和使用,也能更好地幫助我們觸類旁通。
- 說點什么全文代碼:https://gitee.com/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter14
個人QQ:1837307557
battcn開源群(適合新手):391619659
微信公眾號:battcn(歡迎調戲)
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70827.html
摘要:文本將介紹兩種可以優雅的終止線程的方式第一種在多線程模式中有一種叫兩步終止的模式可以優雅的終止線程,這種模式采用了兩個步驟來終止線程,所以叫兩步終止模式。 Java中原來在Thread中提供了stop()方法來終止線程,但這個方法是不安全的,所以一般不建議使用。文本將介紹兩種可以優雅的終止線程的方式... 第一種 在JAVA《Java多線程模式》中有一種叫Two-Phase Term...
摘要:并且,線程池在某些情況下還能動態調整工作線程的數量,以平衡資源消耗和工作效率。同時線程池還提供了對池中工作線程進行統一的管理的相關方法。 開發中經常會遇到各種池(如:連接池,線程池),它們的作用就是為了提高性能及減少開銷,在JDK1.5以后的java.util.concurrent包中內置了很多不同使用場景的線程池,為了更好的理解它們,自己手寫一個線程池,加深印象。 概述 1.什么是...
摘要:如果有其它線程調用了相同對象的方法,那么處于該對象的等待池中的線程就會全部進入該對象的鎖池中,從新爭奪鎖的擁有權。 wait,notify 和 notifyAll,這些在多線程中被經常用到的保留關鍵字,在實際開發的時候很多時候卻并沒有被大家重視,而本文則是對這些關鍵字的使用進行描述。 存在即合理 在java中,每個對象都有兩個池,鎖池(monitor)和等待池(waitset),每個...
摘要:比如用修飾的變量,就會確保變量在修改時,其它線程是可見的。。多核環境中,多個線程分別在不同的中運行,就意味著,多個線程都有可能將變量拷貝到當前運行的里。當線程讀取變量時,它將能看見被線程寫入的東西。 volatile是用來標記一個JAVA變量存儲在主內存(main memory)中,多線程讀寫volatile變量會先從高速緩存中讀取,但是寫入的時候會立即通過內存總線刷到主存,同時內存總...
摘要:但是的語義不足以確保遞增操作的原子性,在多線程的情況下,線程不一定是安全的。檢查某個狀態標記,以判斷是否退出循環某個方法這邊和用普通的變量的區別是,在多線程的情況下,取到后,的值被改變了,判斷會不正確。 多線程為什么是不安全的 這邊簡單的講述一下,參考java并發編程學習之synchronize(一) 當線程A和線程B同時進入num = num + value; 線程A會把num的值...
閱讀 2793·2021-10-11 10:57
閱讀 2402·2021-08-27 16:20
閱讀 1384·2019-08-30 13:03
閱讀 1563·2019-08-30 12:50
閱讀 3336·2019-08-29 14:16
閱讀 1561·2019-08-29 11:12
閱讀 1613·2019-08-28 17:53
閱讀 2893·2019-08-27 10:58