java 任務調度過程中的監控

轉自: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也不會佔用內存空間,提高了系統資源的利用率。

 

 

 

發佈了75 篇原創文章 · 獲贊 18 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章