轉自:http://www.ibm.com/developerworks/cn/java/l-safethread/
對一些代碼按自己的理解修改
在JAVA環境中,一個任務一般是由一個獨立線程來引導實現的,如果在執行過程中,某一個線程發生異常(產生的原因很多,比如軟體升級、運行環境改變、系統資搶佔等),那麼該線程就會停止運行,直到下次任務重新被提交。對於即時環境來說當前任務是失敗的。我們無法預測和完全避免異常的發生,但是可以通過一些技術手段來跟蹤任務的狀態,從而及時發現問題並恢複正常,減少損失。
一個簡單的例子:
A任務每秒執行一個簡單的代數運算 j = 1/ i,並列印結果。我們故意在其中設定了一個異常陷阱,使得執行過程中出現一次被0除的算術異常,下面結合這個例子講述監控原理。
public class ATask extends Thread { /** * A任務定期修改自己的sign標誌,sign是一個布爾變數,理論上只要A沒有死, * 那麼sign肯定是周期變化的(和心跳概念差不多) */ public boolean sign = false; Stakeout m; ATask(Stakeout m) { m = m; start(); } public void run() { try { for (int i = -3; i <= 5; i++) { int j = 1 / i; // 人為設定過程中陷阱 sign = !sign; // 活動狀態 System.out.println("i=" + i + ": status=" + sign); try { sleep(2000); } catch (InterruptedException ie) { System.out.println("A is Interrupted!"); } } m.Keepchecking = false; //A 正常結束後關閉監控線程 System.out.println("A is Ending M"); } catch (Exception e) { System.out.println("A become Exception!"); } } @Override protected void finalize() throws Throwable { System.out.println("線程:"+Thread.currentThread()+" is over by gc~~~~~~~~~~~~~"); }}
為了適應監控,在A任務中相應增加一些可以被監控的狀態和行為:
sign狀態標誌
sign= !sign; 改變狀態
類似於心跳,如果監控方遇到sign都是同一個說明,A出現異常了。
為了監控A,我們設計了一個監控線程Stakeout。Stakeout中定義了一些關鍵邏輯變數:
Keepchecking 持續監控標誌
laststatus 儲存上次監控狀態
maydeadtimes 監控線程可能死亡的計數器
maydeadtimeout 定義判斷線程死亡的邊界條件
deadtimes 監控線程死亡次數的計數器
deadtimeout 定義判斷線程不正常的邊界條件
/** * 監控線程 */public class Stakeout extends Thread { public static boolean Keepchecking = true; // 持續監控標誌 boolean laststatus; //儲存上次監控狀態 int maydeadtimes = 0; //監控線程可能死亡的計數器 int maydeadtimeout = 3;//定義判斷線程死亡的邊界條件 int deadtimes = 0; //監控線程死亡次數的計數器 int deadtimeout = 3; //定義判斷線程不正常的邊界條件 ATask a; Stakeout() { start(); } public void run() { schedule(); while (Keepchecking) { laststatus = a.sign; try { sleep(2000); } catch (InterruptedException e) { System.out.println("M is Interrupted!"); } System.out.println("M read A status = " + a.sign); //如果過了一段時間記錄的狀態與a中的狀態相同,說明可能存線上程死亡的情況 if (laststatus == a.sign) { // 線程可能死亡的次數超過設定值 if (++maydeadtimes >= maydeadtimeout) { //實際線程死亡的次數 if (++deadtimes >= deadtimeout) { System.out.println("Alert! A is unstable, M will stop it"); a = null; break; } else { System.out.println("A is deaded!"); schedule(); System.out.println("M is restarting A!\n____________________________\n"); } } } else { maydeadtimes = 0; } } } /** * 初始化任務 */ private void schedule() { a = new ATask(this); }}
整個監控就是圍繞這些狀態標誌和行為展開的。A任務定期修改自己的sign標誌,sign是一個布爾變數,理論上只要A沒有死,那麼sign肯定是周期變化的(和心跳概念差不多),Stakeout需要做的就是監控sign的變化。為了避免一些偶然因素導致的誤判,我們在Stakeout中設定了一些計數器和邊界值(maydeadtimes和maydeadtimeout),當Stakeout發現A的的sign沒有變化那就認為A是出現假死狀態,如果連續3次出現假死狀態那麼判斷A掛了,嘗試重啟A線程,如果總計出現了3次A掛了的情況,那麼不在重啟了,可以基本肯定不是由於偶然因素引起的,需要對A的代碼或系統的環境進行檢查。Stakeout會發出警示,通知必須要對A進行審查了,然後清空A,自己自動安全退出。如果在核心發送器中設定一個標誌接受Stakeout們的警示,就可以有足夠理由終止其他任務的執行。
可以看見,在A任務發生異常期間,Stakeout承擔了核心發送器的維護功能。特別是當任務數量比較多的情況,核心發送器只能採用排隊方式處理任務異常,而且由於處理異常的複雜程度不同,無法保證對多任務異常的即時處理。
還要考慮正常情況下A和Stakeout的關係。核心發送器通過Stakeout啟動A任務後,Stakeout處於持續監控狀態,當A正常結束任務後,A需要通知Stakeout結束監控,這樣,當A進入休眠狀態後,Stakeout也不會佔用記憶體空間,提高了系統資源的利用率。