線程池的介紹及簡單實現(xiàn)
線程池的介紹及簡單實現(xiàn)![]() |
![]() |
服務(wù)器程序利用線程技術(shù)響應(yīng)客戶請求已經(jīng)司空見慣,可能您認為這樣做效率已經(jīng)很高,但您有沒有想過優(yōu)化一下使用線程的方法。該文章將向您介紹服務(wù)器程序如何利用線程池來優(yōu)化性能并提供一個簡單的線程池實現(xiàn)。
在面向?qū)ο缶幊讨校瑒?chuàng)建和銷毀對象是很費時間的,因為創(chuàng)建一個對象要獲取內(nèi)存資源或者其它更多資源。在Java中更是如此,虛擬機將試圖跟蹤每一個對象,以便能夠在對象銷毀后進行垃圾回收。所以提高服務(wù)程序效率的一個手段就是盡可能減少創(chuàng)建和銷毀對象的次數(shù),特別是一些很耗資源的對象創(chuàng)建和銷毀。如何利用已有對象來服務(wù)就是一個需要解決的關(guān)鍵問題,其實這就是一些"池化資源"技術(shù)產(chǎn)生的原因。比如大家所熟悉的數(shù)據(jù)庫連接池正是遵循這一思想而產(chǎn)生的,本文將介紹的線程池技術(shù)同樣符合這一思想。 目前,一些著名的大公司都特別看好這項技術(shù),并早已經(jīng)在他們的產(chǎn)品中應(yīng)用該技術(shù)。比如IBM的WebSphere,IONA的Orbix 2000在SUN的 Jini中,Microsoft的MTS(Microsoft Transaction Server 2.0),COM+等。 現(xiàn)在您是否也想在服務(wù)器程序應(yīng)用該項技術(shù)?
我所提到服務(wù)器程序是指能夠接受客戶請求并能處理請求的程序,而不只是指那些接受網(wǎng)絡(luò)客戶請求的網(wǎng)絡(luò)服務(wù)器程序。 多線程技術(shù)主要解決處理器單元內(nèi)多個線程執(zhí)行的問題,它可以顯著減少處理器單元的閑置時間,增加處理器單元的吞吐能力。但如果對多線程應(yīng)用不當,會增加對單個任務(wù)的處理時間。可以舉一個簡單的例子: 假設(shè)在一臺服務(wù)器完成一項任務(wù)的時間為T T1 創(chuàng)建線程的時間 T2 在線程中執(zhí)行任務(wù)的時間,包括線程間同步所需時間 T3 線程銷毀的時間 顯然T = T1+T2+T3。注意這是一個極度簡化的假設(shè)。 可以看出T1,T3是多線程本身的帶來的開銷,我們渴望減少T1,T3所用的時間,從而減少T的時間。但一些線程的使用者并沒有注意到這一點,所以在程序中頻繁的創(chuàng)建或銷毀線程,這導(dǎo)致T1和T3在T中占有相當比例。顯然這是突出了線程的弱點(T1,T3),而不是優(yōu)點(并發(fā)性)。 線程池技術(shù)正是關(guān)注如何縮短或調(diào)整T1,T3時間的技術(shù),從而提高服務(wù)器程序性能的。它把T1,T3分別安排在服務(wù)器程序的啟動和結(jié)束的時間段或者一些空閑的時間段,這樣在服務(wù)器程序處理客戶請求時,不會有T1,T3的開銷了。 線程池不僅調(diào)整T1,T3產(chǎn)生的時間段,而且它還顯著減少了創(chuàng)建線程的數(shù)目。在看一個例子: 假設(shè)一個服務(wù)器一天要處理50000個請求,并且每個請求需要一個單獨的線程完成。我們比較利用線程池技術(shù)和不利于線程池技術(shù)的服務(wù)器處理這些請求時所產(chǎn)生的線程總數(shù)。在線程池中,線程數(shù)一般是固定的,所以產(chǎn)生線程總數(shù)不會超過線程池中線程的數(shù)目或者上限(以下簡稱線程池尺寸),而如果服務(wù)器不利用線程池來處理這些請求則線程總數(shù)為50000。一般線程池尺寸是遠小于50000。所以利用線程池的服務(wù)器程序不會為了創(chuàng)建50000而在處理請求時浪費時間,從而提高效率。 這些都是假設(shè),不能充分說明問題,下面我將討論線程池的簡單實現(xiàn)并對該程序進行對比測試,以說明線程技術(shù)優(yōu)點及應(yīng)用領(lǐng)域。
一般一個簡單線程池至少包含下列組成部分。
線程池管理器至少有下列功能:創(chuàng)建線程池,銷毀線程池,添加新任務(wù)創(chuàng)建線程池的部分代碼如下:
注意同步workThreadVector并沒有降低效率,相反提高了效率,請參考Brian Goetz的文章。 銷毀線程池的部分代碼如下:
添加新任務(wù)的部分代碼如下:
工作線程是一個可以循環(huán)執(zhí)行任務(wù)的線程,在沒有任務(wù)時將等待。由于代碼比較多在此不羅列. 任務(wù)接口是為所有任務(wù)提供統(tǒng)一的接口,以便工作線程處理。任務(wù)接口主要規(guī)定了任務(wù)的入口,任務(wù)執(zhí)行完后的收尾工作,任務(wù)的執(zhí)行狀態(tài)等。在文章結(jié)尾有相關(guān)代碼的下載。 以上所描述的線程池結(jié)構(gòu)很簡單,一些復(fù)雜的線程池結(jié)構(gòu)將不再此討論。 在下載代碼中有測試驅(qū)動程序(TestThreadPool),我利用這個測試程序的輸出數(shù)據(jù)統(tǒng)計出下列測試結(jié)果。測試有兩個參數(shù)要設(shè)置:
分別將一個參數(shù)固定,另一個參數(shù)變動以考察兩個參數(shù)所產(chǎn)生的不同結(jié)果。所用測試機器分別為普通PC機(Win2000 JDK1.3.1)和SUN服務(wù)器(Solaris Unix JDK1.3.1),機器配置在此不便指明。 表1:測試數(shù)據(jù)及對應(yīng)結(jié)果
圖1.線程池的尺寸的對服務(wù)器程序的性能影響 ![]() 根據(jù)以上統(tǒng)計數(shù)據(jù)可得出下圖: 圖2.任務(wù)數(shù)對服務(wù)器程序的沖擊 ![]() 數(shù)據(jù)分析如下: 圖1是改變線程池尺寸對服務(wù)器性能的影響,在該測試過程中,服務(wù)器的要完成的任務(wù)數(shù)固定為為5000。從圖1中可以看出合理配置線程池尺寸對于大量任務(wù)處理的效率有非常明顯的提高,但是一旦尺寸選擇不合理(過大或過小)就會嚴重降低影響服務(wù)器性能。理論上"過小"將出現(xiàn)任務(wù)不能及時處理的情況,但在圖表中顯示出某些小尺寸的線程池表現(xiàn)很好,這是因為測試驅(qū)動中有很多線程同步開銷,且這個開銷相對于完成單個任務(wù)的時間是不能忽略的。"過大"則會出現(xiàn)線程間同步開銷太大的問題,而且在線程間切換很耗CPU時間,在圖表顯示的很清楚。可見任何一個好技術(shù),如果濫用都會造成災(zāi)難性后果。 圖2是用不同數(shù)量的任務(wù)來沖擊服務(wù)器程序,在該測試過程中,服務(wù)器線程池尺寸固定為16。可以看出線程池在處理少量任務(wù)時的優(yōu)勢不明顯。所以線程池技術(shù)有一定的適應(yīng)范圍,關(guān)于適用范圍將在后面討論。但對于大量的任務(wù)的處理,線程池的優(yōu)勢表現(xiàn)非常卓越,服務(wù)器程序處理請求的時間雖然有波動,但是其平均值相對小多了。 值得注意的是測試方案中,統(tǒng)計任務(wù)的完成時間沒有包含了創(chuàng)建線程池的時間。在實際線程池工作時,即利用線程池處理任務(wù)時,創(chuàng)建線程池的時間是不必計算在內(nèi)的。 由于測試驅(qū)動程序有很多同步代碼,特別是等待線程執(zhí)行完畢的同步(代碼中為sleepToWait(long l)方法的調(diào)用),這些代碼降低了代碼執(zhí)行效率,這是測試驅(qū)動一個缺點,但這個測試驅(qū)動可以說明線程池相對于簡單使用線程的優(yōu)勢。
簡單線程池存在一些問題,比如如果有大量的客戶要求服務(wù)器為其服務(wù),但由于線程池的工作線程是有限的,服務(wù)器只能為部分客戶服務(wù),其它客戶提交的任務(wù),只能在任務(wù)隊列中等待處理。一些系統(tǒng)設(shè)計人員可能會不滿這種狀況,因為他們對服務(wù)器程序的響應(yīng)時間要求比較嚴格,所以在系統(tǒng)設(shè)計時可能會懷疑線程池技術(shù)的可行性,但是線程池有相應(yīng)的解決方案。調(diào)整優(yōu)化線程池尺寸是高級線程池要解決的一個問題。主要有下列解決方案:
在一些高級線程池中一般提供一個可以動態(tài)改變的工作線程數(shù)目的功能,以適應(yīng)突發(fā)性的請求。一旦請求變少了將逐步減少線程池中工作線程的數(shù)目。當然線程增加可以采用一種超前方式,即批量增加一批工作線程,而不是來一個請求才建立創(chuàng)建一個線程。批量創(chuàng)建是更加有效的方式。該方案還有應(yīng)該限制線程池中工作線程數(shù)目的上限和下限。否則這種靈活的方式也就變成一種錯誤的方式或者災(zāi)難,因為頻繁的創(chuàng)建線程或者短時間內(nèi)產(chǎn)生大量的線程將會背離使用線程池原始初衷--減少創(chuàng)建線程的次數(shù)。 舉例:Jini中的TaskManager,就是一個精巧線程池管理器,它是動態(tài)增加工作線程的。SQL Server采用單進程(Single Process)多線程(Multi-Thread)的系統(tǒng)結(jié)構(gòu),1024個數(shù)量的線程池,動態(tài)線程分配,理論上限32767。
如果不想在線程池應(yīng)用復(fù)雜的策略來保證工作線程數(shù)滿足應(yīng)用的要求,你就要根據(jù)統(tǒng)計學的原理來統(tǒng)計客戶的請求數(shù)目,比如高峰時段平均一秒鐘內(nèi)有多少任務(wù)要求處理,并根據(jù)系統(tǒng)的承受能力及客戶的忍受能力來平衡估計一個合理的線程池尺寸。線程池的尺寸確實很難確定,所以有時干脆用經(jīng)驗值。 舉例:在MTS中線程池的尺寸固定為100。
在一些復(fù)雜的系統(tǒng)結(jié)構(gòu)會采用這個方案。這樣可以根據(jù)不同任務(wù)或者任務(wù)優(yōu)先級來采用不同線程池處理。 舉例:COM+用到了多個線程池。 這三種方案各有優(yōu)缺點。在不同應(yīng)用中可能采用不同的方案或者干脆組合這三種方案來解決實際問題。
下面是我總結(jié)的一些線程池應(yīng)用范圍,可能是不全面的。 線程池的應(yīng)用范圍:
本文只是簡單介紹線程池技術(shù)。可以看出線程池技術(shù)對于服務(wù)器程序的性能改善是顯著的。線程池技術(shù)在服務(wù)器領(lǐng)域有著廣泛的應(yīng)用前景。希望這項技術(shù)能夠應(yīng)用到您的多線程服務(wù)程序中。 |
posted on 2008-09-07 11:10 大石頭 閱讀(171) 評論(0) 編輯 收藏 所屬分類: 多線程