小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

實(shí)現(xiàn) Java 多線程并發(fā)控制框架

 raydian 2007-01-10
Java 提供了語(yǔ)言級(jí)別的線程支持,所以在 Java 中使用多線程相對(duì)于 C,C++ 來(lái)說(shuō)更簡(jiǎn)單便捷,但本文并不是介紹如何在 Java 中使用多線程來(lái)來(lái)解決諸如 Web services, Number crunching 或者 I/O processing 之類的問(wèn)題。在本文中,我們將討論如何實(shí)現(xiàn)一個(gè) Java 多線程的運(yùn)行框架以及我們是如何來(lái)控制線程的并發(fā)同步以及順序執(zhí)行的。

所面臨的問(wèn)題


圖 1. 線程場(chǎng)景
圖 1. 線程場(chǎng)景

這幅圖中節(jié)點(diǎn)代表一個(gè) single Thread,邊代表執(zhí)行的步驟。

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

這其中涉及到了 Java 線程的同步互斥機(jī)制。例如如何讓 T1 在 T2 和 T3 之前運(yùn)行,如何讓 T2 和 T3 都執(zhí)行完畢之后開(kāi)啟 T4 線程。





回頁(yè)首


模型的描述

如何來(lái)描述圖 1 中所示的場(chǎng)景呢?可以采用 XML 的格式來(lái)描述我們的模型。我定義一個(gè)“Thread” element 來(lái)表示線程。

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

其中 ID 是線程的唯一標(biāo)識(shí)符,PRETHREAD 便是該線程的直接先決線程的ID,每個(gè)線程 ID 之間用逗號(hào)隔開(kāi)。

在 Thread 這個(gè) element 里面可以加入你想要該線程執(zhí)行任務(wù)的具體信息。

實(shí)際上模型的描述是解決問(wèn)題非常重要的一個(gè)環(huán)節(jié),整個(gè)線程場(chǎng)景可以用一種一致的形式來(lái)描述,作為 Java 多線程并發(fā)控制框架引擎的輸入。也就是將線程運(yùn)行的模式用 XML 來(lái)描述出來(lái),這樣只用改動(dòng) XML 配置文件就可以更改整個(gè)線程運(yùn)行的模式,不用改動(dòng)任何的源代碼。





回頁(yè)首


兩種實(shí)現(xiàn)機(jī)制

對(duì)于 Java 多線程的運(yùn)行框架來(lái)說(shuō),我們將采用“外”和“內(nèi)”的兩種模式來(lái)實(shí)現(xiàn)。





回頁(yè)首


“外” - 主線程輪詢


圖 2. 靜態(tài)類圖
圖 2. 靜態(tài)類圖

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


圖 3. 線程執(zhí)行場(chǎng)景
圖 3. 線程執(zhí)行場(chǎng)景

TestScenario 作為主線程,作為一個(gè)“外”在的監(jiān)控者,不斷地輪詢 threadEntryList 中所有 ThreadEntry 的狀態(tài),當(dāng) ThreadEntry 接受到 isReady 的查詢后查詢自己的 prerequisite,當(dāng)其中所有的先決線程的狀態(tài)為“正常結(jié)束時(shí)”,它便返回 ready,那么 TestScenario 便會(huì)調(diào)用 ThreadEntry 的 startThread() 方法授權(quán)該 ThreadEntry 運(yùn)行線程,Thread 便通過(guò) run() 方法來(lái)真正執(zhí)行線程。并在正常執(zhí)行完畢后調(diào)用 setPreRequisteState() 方法來(lái)更新整個(gè) Scenario,threadEntryList 中所有 ThreadEntry 中 prerequisite 里面含有該 Thread 的狀態(tài)信息為“正常結(jié)束”。


圖 4. 狀態(tài)更改的過(guò)程
圖 4. 狀態(tài)更改的過(guò)程

如圖 1 中所示的 T4 的先決線程為 T2 和 T3,T2 和 T3 并行執(zhí)行。如圖 4 所示,假設(shè) T2 先執(zhí)行完畢,它會(huì)調(diào)用 setPreRequisteState() 方法來(lái)更新整個(gè) Scenario, threadEntryList 中所有 ThreadEntry 中 prerequisite 里面含有該 T2 的狀態(tài)信息為“正常結(jié)束”。此時(shí),T4 的 prerequisite 中 T2 的狀態(tài)為“正常結(jié)束”,但是 T3 還沒(méi)有執(zhí)行完畢,所以其狀態(tài)為“未完畢”。所以 T4 的 isReady 查詢返回為 false,T4 不會(huì)執(zhí)行。只有當(dāng) T3 執(zhí)行完畢后更新?tīng)顟B(tài)為“正常結(jié)束”后,T4 的狀態(tài)才為 ready,T4 才會(huì)開(kāi)始運(yùn)行。

其余的節(jié)點(diǎn)也以此類推,它們正常執(zhí)行完畢的時(shí)候會(huì)在整個(gè)的 scenario 中廣播該線程正常結(jié)束的信息,由主線程不斷地輪詢各個(gè) ThreadEntry 的狀態(tài)來(lái)開(kāi)啟各個(gè)線程。

這便是采用主控線程輪詢狀態(tài)表的方式來(lái)控制 Java 多線程運(yùn)行框架的實(shí)現(xiàn)方式之一。

優(yōu)點(diǎn):概念結(jié)構(gòu)清晰明了,實(shí)現(xiàn)簡(jiǎn)單。避免采用 Java 的鎖機(jī)制,減少產(chǎn)生死鎖的幾率。當(dāng)發(fā)生異常導(dǎo)致其中某些線程不能正常執(zhí)行完畢的時(shí)候,不會(huì)產(chǎn)生掛起的線程。

缺點(diǎn):采用主線程輪詢機(jī)制,耗費(fèi) CPU 時(shí)間。當(dāng)圖中的節(jié)點(diǎn)太多的(n>??? 而線程單個(gè)線程執(zhí)行時(shí)間比較短的時(shí)候 t<??? 需要進(jìn)一步研究)時(shí)候會(huì)產(chǎn)生線程啟動(dòng)的些微延遲,也就是說(shuō)實(shí)時(shí)性能在極端情況下不好,當(dāng)然這可以另外寫(xiě)一篇文章來(lái)專門(mén)探討。





回頁(yè)首


“內(nèi)” - wait&notify

相對(duì)于“外”-主線程輪詢機(jī)制來(lái)說(shuō),“內(nèi)”采用的是自我控制連鎖觸發(fā)機(jī)制。


圖 5. 鎖機(jī)制的靜態(tài)類圖
圖 5. 鎖機(jī)制的靜態(tài)類圖

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


圖 6. 鎖機(jī)制執(zhí)行順序圖
圖 6. 鎖機(jī)制執(zhí)行順序圖

當(dāng)整個(gè)過(guò)程開(kāi)始運(yùn)行的時(shí)候,我們將所有的線程 start,但是每個(gè)線程所持的 lock 都處于 wait 狀態(tài),線程都會(huì)處于 waiting 的狀態(tài)。此時(shí),我們將 root thread 所持有的自身的 lock notify,這樣 root thread 就會(huì)運(yùn)行起來(lái)。當(dāng) root 的 run 方法執(zhí)行完畢以后。它會(huì)檢查其后續(xù)線程的 waitForCount,并將其值減一。然后再次檢查 waitForCount,如果 waitForCount 等于 0,表示該后續(xù)線程的所有先決線程都已經(jīng)執(zhí)行完畢,此時(shí)我們 notify 該線程的 lock,該后續(xù)線程便可以從 waiting 的狀態(tài)轉(zhuǎn)換成為 running 的狀態(tài)。然后這個(gè)過(guò)程連鎖遞歸的進(jìn)行下去,整個(gè)過(guò)程便會(huì)執(zhí)行完畢。

我們還是以 T2,T3,T4 為例,當(dāng)進(jìn)行 initThreadLock 過(guò)程的時(shí)候,我們可以知道 T4 有兩個(gè)直接先決線程 T2 和 T3,所以 T4 的 waitForCount 等于 2。我們假設(shè) T3 先執(zhí)行完畢,T2 仍然在 running 的狀態(tài),此時(shí)他會(huì)首先遍歷其所有的直接后繼線程,并將他們的 waitForCount 減去 1,此時(shí)他只有一個(gè)直接后繼線程 T4,于是 T4 的 waitForCount 減去 1 以后值變?yōu)?1,不等于 0,此時(shí)不會(huì)將 T4 的 lock notify,T4 繼續(xù) waiting。當(dāng) T2 執(zhí)行完畢之后,他會(huì)執(zhí)行與 T3 相同的步驟,此時(shí) T4 的 waitForCount 等于 0,T2 便 notify T4 的 lock,于是 T4 從 waiting 狀態(tài)轉(zhuǎn)換成為 running 狀態(tài)。其他的節(jié)點(diǎn)也是相似的情況。

當(dāng)然,我們也可以將整個(gè)過(guò)程的信息放在另外的一個(gè)全局對(duì)象中,所有的線程都去查找該全局對(duì)象來(lái)獲取各自所需的信息,而不是采取這種分布式存儲(chǔ)的方式。

優(yōu)點(diǎn):采用 wait&notify 機(jī)制而不采用輪詢的機(jī)制,不會(huì)浪費(fèi)CPU資源。執(zhí)行效率較高。而且相對(duì)于“外”-主線程輪詢的機(jī)制來(lái)說(shuō)實(shí)時(shí)性更好。

缺點(diǎn):采用 Java 線程 Object 的鎖機(jī)制,實(shí)現(xiàn)起來(lái)較為復(fù)雜。而且采取一種連鎖觸發(fā)的方式,如果其中某些線程異常,會(huì)導(dǎo)致所有其后繼線程的掛起而造成整個(gè) scenario 的運(yùn)行失敗。為了防止這種情況的發(fā)生,我們還必須建立一套線程監(jiān)控的機(jī)制來(lái)確保其正常運(yùn)行。





回頁(yè)首


延伸

下面的圖所要表達(dá)的是這樣一種遞歸迭代的概念。例如在圖1 中展示的那樣,T1 這個(gè)節(jié)點(diǎn)表示的是一個(gè)線程。現(xiàn)在,忘掉線程這樣一個(gè)概念,將 T1 抽象為一個(gè)過(guò)程,想象它是一個(gè)銀河系,深入到 T1 中去,它也是一個(gè)許多子過(guò)程的集合,這些子過(guò)程之間的關(guān)系模式就如圖 1 所示那樣,可以用一個(gè)圖來(lái)表示。


圖 7. 嵌套子過(guò)程
圖 7. 嵌套子過(guò)程

可以想象一下這是怎樣的一個(gè)框架,具有無(wú)窮擴(kuò)展性的過(guò)程框架,我們只用定義各個(gè)過(guò)程之間的關(guān)系,我們不用關(guān)心過(guò)程是怎樣運(yùn)行的。事實(shí)上,可以在最終的節(jié)點(diǎn)上指定一個(gè)實(shí)際的工作,比如讀一個(gè)文件,或者submit一個(gè)JCL job,或者執(zhí)行一條sql statement。

其實(shí),按照某種遍歷規(guī)則,完全可以將這種嵌套遞歸的結(jié)構(gòu)轉(zhuǎn)化成為一個(gè)一層扁平結(jié)構(gòu)的圖,而不是原來(lái)的分層的網(wǎng)狀結(jié)構(gòu),但是我們不這樣做的原因是基于以下的幾點(diǎn)考慮:

  1. 如果這樣做,會(huì)導(dǎo)致圖節(jié)點(diǎn)太多,邊太多,令人眼花繚亂。
  2. 不這樣做更主要的原因是每一個(gè)場(chǎng)景,如圖 7 中的 T1,T13,是狀態(tài)聚集的一個(gè)單元,具有高復(fù)用性和可靠性。
  3. 框架是高度抽象的,它實(shí)際的執(zhí)行可以是分布式的,一個(gè)單元可以是一個(gè)系統(tǒng),作為和其他系統(tǒng)的分界標(biāo)志。

實(shí)際上,這是一個(gè)狀態(tài)聚集的層次控制框架,我們可以依賴此框架來(lái)執(zhí)行自主運(yùn)算。我們將在其它的文章中來(lái)討論它的應(yīng)用。





回頁(yè)首


總結(jié)

本文介紹了一種 Java 多線程并發(fā)控制的框架,并給出了其兩種實(shí)現(xiàn)的模型,它們有各自的優(yōu)缺點(diǎn),有各自的適用范圍。當(dāng)需要進(jìn)行 Java 線程的并發(fā)控制的時(shí)候,可以作為參考。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多