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