<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    letter Y A N. G Brass Letter F a n-spo D Pewter Uppercase Letter I N G
    隨筆 - 4, 文章 - 10, 評論 - 2, 引用 - 0
    數據加載中……

    實現 Java 多線程并發控制框架

    所面臨的問題


    圖 1. 線程場景
    圖 1. 線程場景

    這幅圖中節點代表一個 single Thread,邊代表執行的步驟。

    整幅圖代表的意思是,ROOT 線程執行完畢后執行 T1 線程,T1 執行完畢后并發的執行 T2 和 T3。而從 T2 和 T3 指向 T4 的兩條邊表示的是 T4 必須等 T2 和 T3 都執行完畢以后才能開始執行。剩下的步驟以此類推,直到 END 作為整個過程的結束。當然,這只是個簡略的示意圖,可能面對的一個線程場景會有上百個線程。還有,你可以觀察到這整個場景只有一個入口點和一個出口點,這意味著什么?在下文中為你解釋。

    這其中涉及到了 Java 線程的同步互斥機制。例如如何讓 T1 在 T2 和 T3 之前運行,如何讓 T2 和 T3 都執行完畢之后開啟 T4 線程。

    模型的描述

    如何來描述圖 1 中所示的場景呢?可以采用 XML 的格式來描述我們的模型。我定義一個“Thread” element 來表示線程。

    <ThreadList>
    <Thread ID = "thread-id" PRETHREAD = "prethread1, prethread2…"></Thread>
    <Thread ID = "thread-id" PRETHREAD = "prethread3, prethread4…"></Thread>
    </ThreadList>

    其中 ID 是線程的唯一標識符,PRETHREAD 便是該線程的直接先決線程的ID,每個線程 ID 之間用逗號隔開。

    在 Thread 這個 element 里面可以加入你想要該線程執行任務的具體信息。

    實際上模型的描述是解決問題非常重要的一個環節,整個線程場景可以用一種一致的形式來描述,作為 Java 多線程并發控制框架引擎的輸入。也就是將線程運行的模式用 XML 來描述出來,這樣只用改動 XML 配置文件就可以更改整個線程運行的模式,不用改動任何的源代碼。

    兩種實現機制

    對于 Java 多線程的運行框架來說,我們將采用“外”和“內”的兩種模式來實現。


    “外” - 主線程輪詢


    圖 2. 靜態類圖
    圖 2. 靜態類圖 

    Thread 是工作線程。ThreadEntry 是 Thread 的包裝類,prerequisite 是一個 HashMap,它含有 Thread 的先決線程的狀態。如圖1中顯示的那樣,T4 的先決線程是 T2 和 T3,那么 prerequisite 中就包含 T2 和 T3 的狀態。TestScenario 中的 threadEntryList 中包含所有的 ThreadEntry。


    圖 3. 線程執行場景
    圖 3. 線程執行場景 

    TestScenario 作為主線程,作為一個“外”在的監控者,不斷地輪詢 threadEntryList 中所有 ThreadEntry 的狀態,當 ThreadEntry 接受到 isReady 的查詢后查詢自己的 prerequisite,當其中所有的先決線程的狀態為“正常結束時”,它便返回 ready,那么 TestScenario 便會調用 ThreadEntry 的 startThread() 方法授權該 ThreadEntry 運行線程,Thread 便通過 run() 方法來真正執行線程。并在正常執行完畢后調用 setPreRequisteState() 方法來更新整個 Scenario,threadEntryList 中所有 ThreadEntry 中 prerequisite 里面含有該 Thread 的狀態信息為“正常結束”。


    圖 4. 狀態更改的過程
    圖 4. 狀態更改的過程 

    如圖 1 中所示的 T4 的先決線程為 T2 和 T3,T2 和 T3 并行執行。如圖 4 所示,假設 T2 先執行完畢,它會調用 setPreRequisteState() 方法來更新整個 Scenario, threadEntryList 中所有 ThreadEntry 中 prerequisite 里面含有該 T2 的狀態信息為“正常結束”。此時,T4 的 prerequisite 中 T2 的狀態為“正常結束”,但是 T3 還沒有執行完畢,所以其狀態為“未完畢”。所以 T4 的 isReady 查詢返回為 false,T4 不會執行。只有當 T3 執行完畢后更新狀態為“正常結束”后,T4 的狀態才為 ready,T4 才會開始運行。

    其余的節點也以此類推,它們正常執行完畢的時候會在整個的 scenario 中廣播該線程正常結束的信息,由主線程不斷地輪詢各個 ThreadEntry 的狀態來開啟各個線程。

    這便是采用主控線程輪詢狀態表的方式來控制 Java 多線程運行框架的實現方式之一。

    優點:概念結構清晰明了,實現簡單。避免采用 Java 的鎖機制,減少產生死鎖的幾率。當發生異常導致其中某些線程不能正常執行完畢的時候,不會產生掛起的線程。

    缺點:采用主線程輪詢機制,耗費 CPU 時間。當圖中的節點太多的(n>??? 而線程單個線程執行時間比較短的時候 t<??? 需要進一步研究)時候會產生線程啟動的些微延遲,也就是說實時性能在極端情況下不好,當然這可以另外寫一篇文章來專門探討。


    “內” - wait&notify

    相對于“外”-主線程輪詢機制來說,“內”采用的是自我控制連鎖觸發機制。


    圖 5. 鎖機制的靜態類圖
    圖 5. 鎖機制的靜態類圖 

    Thread 中的 lock 為當前 Thread 的 lock,lockList 是一個 HashMap,持有其后繼線程的 lock 的引用,getLock 和 setLock 可以對 lockList 中的 Lock 進行操作。其中很重要的一個成員是 waitForCount,這是一個引用計數。表明當前線程正在等待的先決線程的個數,例如圖 1 中所示的 T4,在初始的情況下,他等待的先決線程是 T2 和 T3,那么它的 waitForCount 等于 2。


    圖 6. 鎖機制執行順序圖
    圖 6. 鎖機制執行順序圖 

    當整個過程開始運行的時候,我們將所有的線程 start,但是每個線程所持的 lock 都處于 wait 狀態,線程都會處于 waiting 的狀態。此時,我們將 root thread 所持有的自身的 lock notify,這樣 root thread 就會運行起來。當 root 的 run 方法執行完畢以后。它會檢查其后續線程的 waitForCount,并將其值減一。然后再次檢查 waitForCount,如果 waitForCount 等于 0,表示該后續線程的所有先決線程都已經執行完畢,此時我們 notify 該線程的 lock,該后續線程便可以從 waiting 的狀態轉換成為 running 的狀態。然后這個過程連鎖遞歸的進行下去,整個過程便會執行完畢。

    我們還是以 T2,T3,T4 為例,當進行 initThreadLock 過程的時候,我們可以知道 T4 有兩個直接先決線程 T2 和 T3,所以 T4 的 waitForCount 等于 2。我們假設 T3 先執行完畢,T2 仍然在 running 的狀態,此時他會首先遍歷其所有的直接后繼線程,并將他們的 waitForCount 減去 1,此時他只有一個直接后繼線程 T4,于是 T4 的 waitForCount 減去 1 以后值變為 1,不等于 0,此時不會將 T4 的 lock notify,T4 繼續 waiting。當 T2 執行完畢之后,他會執行與 T3 相同的步驟,此時 T4 的 waitForCount 等于 0,T2 便 notify T4 的 lock,于是 T4 從 waiting 狀態轉換成為 running 狀態。其他的節點也是相似的情況。

    當然,我們也可以將整個過程的信息放在另外的一個全局對象中,所有的線程都去查找該全局對象來獲取各自所需的信息,而不是采取這種分布式存儲的方式。

    優點:采用 wait&notify 機制而不采用輪詢的機制,不會浪費CPU資源。執行效率較高。而且相對于“外”-主線程輪詢的機制來說實時性更好。

    缺點:采用 Java 線程 Object 的鎖機制,實現起來較為復雜。而且采取一種連鎖觸發的方式,如果其中某些線程異常,會導致所有其后繼線程的掛起而造成整個 scenario 的運行失敗。為了防止這種情況的發生,我們還必須建立一套線程監控的機制來確保其正常運行。


    延伸

    下面的圖所要表達的是這樣一種遞歸迭代的概念。例如在圖1 中展示的那樣,T1 這個節點表示的是一個線程。現在,忘掉線程這樣一個概念,將 T1 抽象為一個過程,想象它是一個銀河系,深入到 T1 中去,它也是一個許多子過程的集合,這些子過程之間的關系模式就如圖 1 所示那樣,可以用一個圖來表示。


    圖 7. 嵌套子過程
    圖 7. 嵌套子過程 

    可以想象一下這是怎樣的一個框架,具有無窮擴展性的過程框架,我們只用定義各個過程之間的關系,我們不用關心過程是怎樣運行的。事實上,可以在最終的節點上指定一個實際的工作,比如讀一個文件,或者submit一個JCL job,或者執行一條sql statement。

    其實,按照某種遍歷規則,完全可以將這種嵌套遞歸的結構轉化成為一個一層扁平結構的圖,而不是原來的分層的網狀結構,但是我們不這樣做的原因是基于以下的幾點考慮:

    1. 如果這樣做,會導致圖節點太多,邊太多,令人眼花繚亂。
    2. 不這樣做更主要的原因是每一個場景,如圖 7 中的 T1,T13,是狀態聚集的一個單元,具有高復用性和可靠性。
    3. 框架是高度抽象的,它實際的執行可以是分布式的,一個單元可以是一個系統,作為和其他系統的分界標志。

    實際上,這是一個狀態聚集的層次控制框架,我們可以依賴此框架來執行自主運算。我們將在其它的文章中來討論它的應用。



    總結

    本文介紹了一種 Java 多線程并發控制的框架,并給出了其兩種實現的模型,它們有各自的優缺點,有各自的適用范圍。當需要進行 Java 線程的并發控制的時候,可以作為參考。



    參考資料



    關于作者

    陳威,華中科技大學碩士,IBM CSDL Software Engineer,所在的 Team 是 DB2 for z/OS。聯系方式:chenwbj@cn.ibm.com


     

     

    posted on 2008-10-02 23:12 rainman 閱讀(631) 評論(1)  編輯  收藏 所屬分類: java多線程

    評論

    # re: 實現 Java 多線程并發控制框架  回復  更多評論   

    不知道您看懂了這篇文章了沒有?反正我是看不懂,不知道你能不能實現份代碼?如果能的話能話,能不能給我一份?謝謝啦 junmingabc@gmail.com
    2010-07-25 14:11 | 小陳
    主站蜘蛛池模板: 亚洲Av熟妇高潮30p| 亚洲国产精品无码专区在线观看| 亚洲精品在线播放| aa级一级天堂片免费观看| 免费看国产一级片| 亚洲成av人片天堂网无码】| 成年女人色毛片免费看| 亚洲乱亚洲乱妇24p| 四虎成人免费大片在线| 久久亚洲精品无码av| 亚洲M码 欧洲S码SSS222| 亚洲成无码人在线观看| 久久久久久久国产免费看| 久久久久亚洲精品无码网址| 中文日本免费高清| 亚洲人成网址在线观看| 希望影院高清免费观看视频| 亚洲午夜久久久久妓女影院| 成年免费a级毛片免费看无码| 久久久亚洲欧洲日产国码农村| 97精品免费视频| 亚洲VA中文字幕无码毛片| 叮咚影视在线观看免费完整版| 亚洲国产老鸭窝一区二区三区| 中文字幕免费在线看线人 | 亚洲Aⅴ无码一区二区二三区软件 亚洲AⅤ视频一区二区三区 | 亚洲精品永久在线观看| 无码欧精品亚洲日韩一区夜夜嗨 | 成年大片免费高清在线看黄| 在线观看亚洲av每日更新| 日韩免费视频一区二区| 久久精品国产亚洲AV久| 四虎永久在线精品免费影视| 国产免费牲交视频免费播放| 亚洲黑人嫩小videos| 免费一级特黄特色大片在线 | 一级毛片完整版免费播放一区| 亚洲热妇无码AV在线播放| 日本片免费观看一区二区| 日本亚洲欧美色视频在线播放| 自拍偷自拍亚洲精品情侣|