在一系列關(guān)聯(lián)的多任務(wù)的實時環(huán)境中,如果有一個任務(wù)發(fā)生失敗,可能導(dǎo)致所有任務(wù)產(chǎn)生連鎖反應(yīng),從而造成調(diào)度失控的局面。特別是對于核心控制設(shè)備尤其重要,為了解決這個問題,必須對每個任務(wù)進行實時監(jiān)控。
?
在一系列關(guān)聯(lián)的多任務(wù)的實時環(huán)境中,如果有一個任務(wù)發(fā)生失敗,可能導(dǎo)致所有任務(wù)產(chǎn)生連鎖反應(yīng),從而造成調(diào)度失控的局面。特別是對于核心控制設(shè)備尤其重要,為了解決這個問題,必須對每個任務(wù)進行實時監(jiān)控。
?
在JAVA環(huán)境中,一個任務(wù)一般是由一個獨立線程來引導(dǎo)實現(xiàn)的,獨立線程可能調(diào)用一系列子線程。如果在執(zhí)行過程中,某一個線程發(fā)生異常(產(chǎn)生的原因很多,比如軟件升級、運行環(huán)境改變、系統(tǒng)資搶占等),那么該線程就會停止運行,直到下次任務(wù)重新被提交。對于實時環(huán)境來說當(dāng)前任務(wù)是失敗的。我們無法預(yù)測和完全避免異常的發(fā)生,但是可以通過一些技術(shù)手段來跟蹤任務(wù)的狀態(tài),從而及時發(fā)現(xiàn)問題并恢復(fù)正常,減少損失。
?
?
?
對于一個實時任務(wù)而言,執(zhí)行效率是非常關(guān)鍵的,這意味著不可能考慮用非常復(fù)雜的方式來實現(xiàn)任務(wù)監(jiān)控,即使這樣可以做的比較完善,同時監(jiān)控代碼本身也會引入一些異常,這就要求監(jiān)控程序必須簡單可靠,能夠發(fā)現(xiàn)大多數(shù)問題,并能及時處理。
一個可能的簡單實現(xiàn)。
我們對每個任務(wù)加上一個監(jiān)控的"殼",調(diào)度程序調(diào)用這個"殼"來完成對具體任務(wù)的引導(dǎo)和監(jiān)控,相當(dāng)于每個任務(wù)具有自治能力。這樣做的好處有:
- 分散控制。不需要修改調(diào)度主體程序,不增加調(diào)度過程的復(fù)雜度;
- 控制靈活,安全性高。對于不同任務(wù)可定義不同控制方式和控制參數(shù),封裝在任務(wù)內(nèi)部,靈活性好,對個別任務(wù)控制代碼的修改不會影響其他任務(wù),即任務(wù)控制實現(xiàn)松藕合,避免錯誤擴散。適合團隊開發(fā);
- 維護簡單,升級方便。對于新的任務(wù)加入也無需改變原來調(diào)度程序的結(jié)構(gòu),只需修改任務(wù)表及相關(guān)參數(shù),這樣在性能提高的同時也簡化了軟件升級過程中的代碼維護量。
?
?
每個線程理論上有四種狀態(tài):
new | 線程對象已經(jīng)創(chuàng)建,但尚未啟動,不可運行 |
runnable | 一旦有時間分片機制有空閑的CPU周期,線程立即開始運行 |
dead | 從run()方法退出后,一個線程即消亡 |
blocked | 線程可運行,但有某種東西阻礙了它。調(diào)度機制不給它分配任何CPU時間,直到它進入runnable狀態(tài) |
在實際操作中,為了便于描述,我們重新規(guī)定了線程的狀態(tài):
Actived | 線程已被激活,處于運行狀態(tài) |
Sleeping | 線程完成一個特定任務(wù)后退出,進入休眠狀態(tài) |
Dead | 線程運行過程中發(fā)生異常,終止運行,處于死亡狀態(tài) |
根據(jù)上述理論,我們只需要對Actived狀態(tài)的線程進行監(jiān)控,也只有對Actived狀態(tài)監(jiān)控才有意義,這是對監(jiān)控模塊做出邏輯簡化。那么如何實現(xiàn)監(jiān)控模塊對具體任務(wù)的監(jiān)控呢?
對具體任務(wù)的監(jiān)控方式有多種,根據(jù)任務(wù)的不同,需要采用不同的監(jiān)控代碼,但是在結(jié)構(gòu)上基本相同。這和類的重載概念有點相似。本文附有一個簡單的例子。
A任務(wù)每秒執(zhí)行一個簡單的代數(shù)運算 j = 1/ i,并打印結(jié)果。我們故意在其中設(shè)置了一個異常陷阱,使得執(zhí)行過程中出現(xiàn)一次被0除的算術(shù)異常,下面結(jié)合這個例子講述監(jiān)控原理。
為了監(jiān)控A,我們設(shè)計了一個監(jiān)控線程M。M中定義了一些關(guān)鍵邏輯變量:
Keepchecking | 持續(xù)監(jiān)控標(biāo)志 |
laststatus | 保存上次監(jiān)控狀態(tài) |
maydeadtimes | 監(jiān)控線程可能死亡的計數(shù)器 |
maydeadtimeout | 定義判斷線程死亡的邊界條件 |
deadtimes | 監(jiān)控線程死亡次數(shù)的計數(shù)器 |
deadtimeout | 定義判斷線程不正常的邊界條件 |
為了適應(yīng)監(jiān)控,在A任務(wù)中相應(yīng)增加一些可以被監(jiān)控的狀態(tài)和行為:
dead | 死狀態(tài)標(biāo)志 |
dead = !dead; | 改變狀態(tài) |
整個監(jiān)控就是圍繞這些狀態(tài)標(biāo)志和行為展開的。A任務(wù)定期修改自己的dead標(biāo)志,dead是一個布爾變量,理論上只要A沒有死,那么dead肯定是周期變化的(和心跳概念差不多),M需要做的就是監(jiān)控dead的變化。為了避免一些偶然因素導(dǎo)致的誤判,我們在M中設(shè)置了一些計數(shù)器和邊界值(maydeadtimes和maydeadtimeout),當(dāng)M發(fā)現(xiàn)A的可能死亡次數(shù)超過一定限制后,判斷A已死亡,不在繼續(xù)等待了,作為實時處理,首先注銷A的實例,然后重新啟動A(和我們計算機死機的復(fù)位很像),然后進入新一輪監(jiān)控。
如果是因為系統(tǒng)偶然因素導(dǎo)致A死亡,那么在隨后的新的任務(wù)啟動過程中一般可以順利完成。但是萬一由于環(huán)境參數(shù)改變或軟件升級存在版本缺陷,A可能始終會產(chǎn)生異常,那么M是否需要耐心地監(jiān)控下去呢?一個形象的例子是:如果你連續(xù)3次開機都失敗,你是否會懷疑機器有問題?當(dāng)然,你會,那么M也應(yīng)該會。
為了對A任務(wù)重復(fù)多次死亡有一個統(tǒng)計,M中又引入了另外對計數(shù)器和邊界值(deadtimes和deadtimeout),和你開計算機的過程一樣,如果連續(xù)n次都發(fā)現(xiàn)A有問題,可以基本肯定不是由于偶然因素引起的,需要對A的代碼或系統(tǒng)的環(huán)境進行檢查。M會發(fā)出告警,通知必須要對A進行審查了,然后清空A,自己自動安全退出。如果在核心調(diào)度程序中設(shè)置一個標(biāo)志接受M們的告警,就可以有足夠理由終止其他任務(wù)的執(zhí)行。可以看見,在A任務(wù)發(fā)生異常期間,M承擔(dān)了核心調(diào)度程序的維護功能。特別是當(dāng)任務(wù)數(shù)量比較多的情況,核心調(diào)度程序只能采用排隊方式處理任務(wù)異常,而且由于處理異常的復(fù)雜程度不同,無法保證對多任務(wù)異常的實時處理。
還要考慮正常情況下A和M的關(guān)系。核心調(diào)度程序通過M啟動A任務(wù)后,M處于持續(xù)監(jiān)控狀態(tài),當(dāng)A正常結(jié)束任務(wù)后,A需要通知M結(jié)束監(jiān)控,這樣,當(dāng)A進入休眠狀態(tài)后,M也不會占用內(nèi)存空間,提高了系統(tǒng)資源的利用率。
通過以上描述,可以看到,上述監(jiān)控思想具有清晰的概念和可操作性,占用資源少,為保證系統(tǒng)連續(xù)穩(wěn)定運行創(chuàng)造了條件。
具體代碼實現(xiàn)附后。
運行結(jié)果如下:
?
異常情況 | 正常情況 |
i=-3: status=true
M read A status = true i=-2: status=false M read A status = false i=-1: status=true M read A status = true A become Exception! M read A status = true M read A status = true M read A status = true A is deaded! M is restarting A! ____________________________ i=-3: status=false M read A status = false i=-2: status=true M read A status = true i=-1: status=false M read A status = false A become Exception! M read A status = false M read A status = false M read A status = false A is deaded! M is restarting A! ____________________________ i=-3: status=true M read A status = true i=-2: status=false M read A status = false i=-1: status=true M read A status = true A become Exception! M read A status = true M read A status = true M read A status = true Alert! A is unstable, M will stop it (結(jié)束) |
i=1: status=true
M read A status = true i=2: status=false M read A status = false i=3: status=true M read A status = true i=4: status=false M read A status = false i=5: status=true M read A status = true A is Ending M M read A status = true (結(jié)束) |
?
![]() package safethread; // 核心調(diào)度程序 public class mythread { public mythread() { } public static void main(String[] args) { M m = new M(); } } // A 任務(wù)線程 class A extends Thread { public static boolean dead = false; M m; A(M m){ m = m; start(); } public void run(){ try{ for(int i=-3;i<= 5;i++){ int j=1/i; // 人為設(shè)置過程中陷阱 dead = !dead; // 活動狀態(tài) System.out.println("i=" + i + ": status=" + dead); try{ sleep(2000); } catch(InterruptedException ie){ System.out.println("A is Interrupted!"); } } m.Keepchecking = false; //A 正常結(jié)束后關(guān)閉監(jiān)控線程 System.out.println("A is Ending M"); } catch(Exception e){ System.out.println("A become Exception!"); } } } // 監(jiān)控線程 class M extends Thread{ public static boolean Keepchecking = true; // 持續(xù)監(jiān)控標(biāo)志 boolean laststatus; //保存上次監(jiān)控狀態(tài) int maydeadtimes = 0; //監(jiān)控線程可能死亡的計數(shù)器 int maydeadtimeout = 3;//定義判斷線程死亡的邊界條件 int deadtimes = 0; //監(jiān)控線程死亡次數(shù)的計數(shù)器 int deadtimeout = 3; //定義判斷線程不正常的邊界條件 A a; M(){start();} public void run(){ schedule(); while(Keepchecking){ laststatus = a.dead; try{ sleep(2000); } catch(InterruptedException e){ System.out.println("M is Interrupted!"); } System.out.println("M read A status = " + a.dead); if(laststatus == a.dead ){ 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 a = new A(this); } }? |
|
?
?
通過給制定任務(wù)線程增加監(jiān)控線程,可以很好地解決實時多任務(wù)環(huán)境下的安全監(jiān)控問題,同時避免了核心調(diào)度線程事務(wù)過分復(fù)雜的問題。實踐證明,該方法復(fù)雜度小,占用資源少,運行可靠,適合復(fù)雜條件下的多任務(wù)環(huán)境。
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯(lián)系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
