下載本文檔
版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報或認(rèn)領(lǐng)
文檔簡介
1、 4/4Tomcat線程池實現(xiàn)簡介 Tomcat線程池實現(xiàn)簡介 目前市場上常用的開源Java Web容器有Tomcat、Resin和Jetty。其中Resin從V3.0后需要購買才能用于商業(yè)目的,而其他兩種則是純開源的??梢苑謩e從他們的網(wǎng)站上下載最新的二進(jìn)制包和源代碼。 作為Web容器,需要承受較高的訪問量,能夠同時響應(yīng)不同用戶的請求,能夠在惡劣環(huán)境下保持較高的穩(wěn)定性和健壯性。在HTTP服務(wù)器領(lǐng)域,Apache HTTPD的效率是最高的,也是最為穩(wěn)定的,但它只能處理靜態(tài)頁面的請求,如果需要支持動態(tài)頁面請求,則必須安裝相應(yīng)的插件,比如mod_perl可以處理Perl腳本,mod_python可以
2、處理Python腳本。 上面介紹的三中Web容器,都是使用Java編寫的HTTP服務(wù)器,當(dāng)然他們都可以嵌到Apache 中使用,也可以獨立使用。分析它們處理客戶請求的方法有助于了解Java多線程和線程池的實現(xiàn)方法,為設(shè)計強(qiáng)大的多線程服務(wù)器打好基礎(chǔ)。 Tomcat是使用最廣的Java Web容器,功能強(qiáng)大,可擴(kuò)展性強(qiáng)。最新版本的Tomcat(5.5.17)為了提高響應(yīng)速度和效率,使用了Apache Portable Runtime(APR)作為最底層,使用了APR 中包含Socket、緩沖池等多種技術(shù),性能也提高了。APR也是Apache HTTPD的最底層。可想而知,同屬于ASF(Apache
3、 Software Foundation)中的成員,互補(bǔ)互用的情況還是很多的,雖然使用了不同的開發(fā)語言。 Tomcat 的線程池位于tomcat-util.jar文件中,包含了兩種線程池方案。方案一:使用APR 的Pool技術(shù),使用了JNI;方案二:使用Java實現(xiàn)的ThreadPool。這里介紹的是第二種。如果想了解APR的Pool技術(shù),可以查看APR的源代碼。 ThreadPool默認(rèn)創(chuàng)建了5個線程,保存在一個200維的線程數(shù)組中,創(chuàng)建時就啟動了這些線程,當(dāng)然在沒有請求時,它們都處理“等待”狀態(tài)(其實就是一個while循環(huán),不停的等待notify)。如果有請求時,空閑線程會被喚醒執(zhí)行用戶的
4、請求。 具體的請求過程是:服務(wù)啟動時,創(chuàng)建一個一維線程數(shù)組(maxThread200個),并創(chuàng)建空閑線程(minSpareThreads5個)隨時等待用戶請求。當(dāng)有用戶請求時,調(diào)用threadpool.runIt(ThreadPoolRunnable)方法,將一個需要執(zhí)行的實例傳給ThreadPool中。其中用戶需要執(zhí)行的實例必須實現(xiàn)ThreadPoolRunnable接口。ThreadPool 首先查找空閑的線程,如果有則用它運(yùn)行要執(zhí)行ThreadPoolRunnable;如果沒有空閑線程并且沒有超過maxThreads,就一次性創(chuàng)建minSpareThreads個空閑線程;如果已經(jīng)超過了m
5、axThreads了,就等待空閑線程了。總之,要找到空閑的線程,以便用它執(zhí)行實例。找到后,將該線程從線程數(shù)組中移走。接著喚醒已經(jīng)找到的空閑線程,用它運(yùn)行執(zhí)行實例(ThreadPoolRunnable)。運(yùn)行完ThreadPoolRunnable后,就將該線程重新放到線程數(shù)組中,作為空閑線程供后續(xù)使用。 由此可以看出,Tomcat的線程池實現(xiàn)是比較簡單的,ThreadPool.java也只有840行代碼。用一個一維數(shù)組保存空閑的線程,每次以一個較小步伐(5個)創(chuàng)建空閑線程并放到線程池中。使用時從數(shù)組中移走空閑的線程,用完后,再“歸還”給線程池。ThreadPool提供的僅僅是線程池的實現(xiàn),而如何
6、使用線程池也是有很大學(xué)問的。讓我們看看Tomcat是如何使用ThreadPool的吧。 Tomcat有兩種EndPoint,分別是AprEndpoint和PoolTcpEndpoint。前者自己實現(xiàn)了一套線 程池(其實這和Tomcat 老版本的方案是相同的,至今Tomcat中還保留著老版本的線程池,PoolTcpEndpoint也有類似的代碼,通過“策略”可以選擇不同的線程池方案)。我們只關(guān)注PoolTcpEndpoint如何使用ThreadPool的。 首先,PoolTcpEndpoint創(chuàng)建了一個ThreadPoolRunnable實例LeaderFollowerWorkerThread,
7、實際上該實例就是接收(Accept)并處理(Process)用戶socket 請求。接著將該實例放進(jìn)ThreadPool中并運(yùn)行,此時就可以接收用戶的請求了。 當(dāng)有Socket請求時,LeaderFollowerWorkerThread首先獲得了Socket實例,注意此時LeaderFollowerWorkerThread并沒有急著處理該Socket,而是在響應(yīng)Socket消息前,再次將LeaderFollowerWorkerThread放進(jìn)ThreadPool中,從而它(當(dāng)然是另外一個線程了)可以繼續(xù)處理其他用戶的Socket 請求;接著,擁有Socket的LeaderFollowerWor
8、kerThread再來處理該用戶的Socket請求。 整個過程與傳統(tǒng)的處理用戶Socket請求是不同的,也和Tomcat老版本不同。傳統(tǒng)的處理方法是:有一個后臺運(yùn)行的監(jiān)聽線程負(fù)責(zé)統(tǒng)一處理接收(注意只是“接收”)Socket請求,當(dāng)有新的Socket請求時,將它賦值給一個Worker線程(通常是喚醒該線程),并有后者處理Socket請求,監(jiān)聽線程繼續(xù)等待其他Socket請求。所以整個過程中有一個從Listener到Worker 切換的過程。而新版本Tomcat很有創(chuàng)造性的使用了另外一種方法,正如前文所描述的,接收和處理某個用戶Socket請求的始終是由一個線程全程負(fù)責(zé),沒有切換到其他線程處理,少
9、了這種線程間的切換是否更有效率呢?我還不能確認(rèn)。不過這種使用方式確實有別于傳統(tǒng)模式,有種耳目一新的感覺。 除了Tomcat外,Jetty是另外一個重要的Java Web容器,號稱“最小的”Web容器,從Jetty 的源代碼規(guī)??梢钥闯鏊_實比較小。而且它的ThreadPool的實現(xiàn)也非常簡單,整個代碼ThreadPool代碼只有450行左右,可見小巧之極。 ThreadPool代碼位于com.mortbty.thread包中,其中最重要的方法是dispatch()和內(nèi)部類PoolThread。顧名思義,dispatch方法主要是將Runnable實例派給線程池中的空閑PoolThread,由后
10、者運(yùn)行Runnable。 還是看看整個過程吧。首先,ThreadPool創(chuàng)建_minThreads個空閑PoolThread,并把它們添加到空閑線程隊列中。當(dāng)需要運(yùn)行Runnable時,首先查找是否有空閑的PoolThread,如果有空閑的,這由它處理;如果沒有并且PoolThread并沒有超過_maxThreads個時,則創(chuàng)建一個新的PoolThread,并由這個新創(chuàng)建的PoolThread運(yùn)行Runnable;如果PoolThread超過了_maxThreads,則一直等待有空閑的PoolThread出現(xiàn)。在PoolThread運(yùn)行之前,必須把該P(yáng)oolThread從空閑線程隊列中移走。
11、再來看看PoolThread的實現(xiàn)吧。和所有的Worker線程一樣,用一個while(flag)wait();循環(huán)等待Runnable的到來,當(dāng)有Runnable被ThreadPool.dispatch()時,該P(yáng)oolThread就運(yùn)行Runnable;當(dāng)運(yùn)行完成后,再“歸還”給空閑線程隊列。Jetty如何使用ThreadPool?整個Jetty只使用了一個ThreadPool實例,具體入口在org.mortbay.jetty.Server中被實例化的,Connector中也使用Server的ThreadPool處理用戶的Socket 請求。Connector是處理用戶Socket請求的入口
12、,一個Connector創(chuàng)建_acceptors個Acceptor,由Acceptor處理用戶Socket 請求時,當(dāng)有Socket請求時,就創(chuàng)建一個Connection放到線程池中處理,而Acceptor繼續(xù)處理其他的Socket請求。這是個傳統(tǒng)的Listener和Worker處理方式。 在這些Java Web容器中,Resin算得上很特別的,小巧穩(wěn)定,而且效率很高。在這些Java Web 容器中,算它的效率最高了。很多大型的網(wǎng)站中都能找到它的身影。Resin從3.0版本后開 始走“特色”的開源路,與MySql很相似如果用于商業(yè)目的,則需要買它的License。但對于個人研究而言,這已經(jīng)不錯
13、了,在網(wǎng)站上可以下載除了涉及License的源代碼外其他所有代碼。 說Resin特別,還主要是由于它的性能出眾,即使在很多企業(yè)級應(yīng)用中也能派上用場。Resin 的數(shù)據(jù)庫連接池做的很不錯,效率非常高。不過這里我們討論它的線程池,看看有何特別之處。 Resin的ThreadPool位于com.caucho.util.ThreadPool中,不過這個類的命名有點蹊蹺,更恰當(dāng)?shù)拿荰hreadPoolItem,因為它確實只是一個普通的Thread。那線程調(diào)度何管理在哪里呢?也在這個類中,不過都是以靜態(tài)函數(shù)方式提供的,所以這個類起到了兩重作用:線程池調(diào)度和Worker線程。也由于這種原因,Resin實
14、例中只有一個線程池,不像Tomcat和Jetty 可以同時運(yùn)行多個線程池,不過對于一個系統(tǒng)而言,一個線程池足夠了。 和其他線程池實現(xiàn)方式不同的是,Resin采用鏈表保存線程。如果有請求時,就將Head移走并喚醒該線程;待運(yùn)行完成后,該線程就變成空閑狀態(tài)并且被添加到鏈表的Head部分。另外,每一個線程運(yùn)行時都要判斷當(dāng)前空閑線程數(shù)是否超過_minSpareThreads,如果超過了,該線程就會退出(狀態(tài)變成Dead),也從鏈表中刪除。 Resin如何使用該ThreadPool?所有需要用線程池的地方,只需調(diào)用ThreadPool. Schedule(Runnable)即可。該方法就是一個靜態(tài)函數(shù),
15、顧名思義,就是將Runnable加到ThreadPool中待運(yùn)行。 Resin使用的還是傳統(tǒng)方法:監(jiān)聽線程(com.caucho.server.port.Port),系統(tǒng)中可以有多個Port 實例,前提端口號不同,比如有80和8080端口;另外就是Worker線程,其實就是ThreadPool 中的空閑線程。Port本身是一個Thread,在啟動時,會在ThreadPool中運(yùn)行5個線程TcpConnection同時等待用戶請求,當(dāng)有用戶請求時,其中的一個會處理。其他繼續(xù)等待。當(dāng)處理用戶請求完成后,還可以重用這些TcpConnection,這與Jetty的有所不同,Jetty是當(dāng)有用戶請求時,
16、才創(chuàng)建連接,處理完成后也不會重用這些連接,效率會稍差一些。 另外Resin有兩個后臺運(yùn)行線程:ThreadLauncher和ScheduleThread,前者負(fù)責(zé)當(dāng)空閑線程小于最小空閑線程時創(chuàng)建新的線程;而后者則負(fù)責(zé)運(yùn)行實際的Runnable。我覺得有的負(fù)責(zé),沒有必要用一個線程來創(chuàng)建新線程,多此一舉。不過ScheduleThread是必須的,因為它就是Worker線程。 介紹了tomcat、jetty和resin三種Java Web容器的線程池后,按照慣例應(yīng)該比較它們的優(yōu)缺點。不過先總結(jié)線程池的特點。 線程池作為提高程序處理數(shù)據(jù)能力的一種方案,應(yīng)用非常廣泛。大量的服務(wù)器都或多或少的使用到了線程
17、池技術(shù),不管是用Java還是C+實現(xiàn),線程池都有如下的特點: 線程池一般有三個重要參數(shù): 1. 最大線程數(shù)。在程序運(yùn)行的任何時候,線程數(shù)總數(shù)都不會超過這個數(shù)。如果請求數(shù)量超過最大數(shù)時,則會等待其他線程結(jié)束后再處理。 2. 最大共享線程數(shù),即最大空閑線程數(shù)。如果當(dāng)前的空閑線程數(shù)超過該值,則多余的線程會被殺掉。 3. 最小共享線程數(shù),即最小空閑線程數(shù)。如果當(dāng)前的空閑數(shù)小于該值,則一次性創(chuàng)建這個數(shù)量的空閑線程,所以它本身也是一個創(chuàng)建線程的步長。 線程池有兩個概念: 1. Worker線程。工作線程主要是運(yùn)行執(zhí)行代碼,有兩種狀態(tài):空閑狀態(tài)和運(yùn)行狀態(tài)。在空閑狀態(tài)時,類似“休眠”,等待任務(wù);處理運(yùn)行狀態(tài)時
18、,表示正在運(yùn)行任務(wù)(Runnable)。 2. 輔助線程。主要負(fù)責(zé)監(jiān)控線程池的狀態(tài):空閑線程是否超過最大空閑線程數(shù)或者小于最小空閑線程數(shù)等。如果不滿足要求,就調(diào)整之。 如果按照上述標(biāo)準(zhǔn)去考察這三個容器就會發(fā)現(xiàn):Tomcat實現(xiàn)的線程池是最完備的,Resin次之,而Jetty最為簡單。Jetty沒有控制空閑線程的數(shù)量,可能最后空閑線程數(shù)會達(dá)到最大線程數(shù),影像性能,畢竟即使是休眠線程也會耗費(fèi)CPU時鐘的。 談?wù)凴esin的線程池。Resin的實現(xiàn)比Tomcat復(fù)雜些。也有上述三個參數(shù),也有兩個概念,這與Tomcat相當(dāng)。但考慮到如何使用ThreadPool時,Resin也要復(fù)雜些。 或許由于Resin的ThreadPool是單間模式的,所有使用ThreadPool
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 環(huán)境健康大數(shù)據(jù)的風(fēng)險評估
- 電子集團(tuán)市場專員崗位知識考試題集含答案
- 企業(yè)形象策劃公司項目經(jīng)理的職責(zé)與面試題
- 特殊項目運(yùn)動員的傷病防控方案
- 節(jié)能洗衣機(jī)項目可行性研究報告(總投資3000萬元)(14畝)
- 深度解析(2026)《GBT 18905.6-2002軟件工程 產(chǎn)品評價 第6部分評價模塊的文檔編制》
- 風(fēng)險控制專員考核標(biāo)準(zhǔn)及辦法
- 市場營銷總監(jiān)面試題及品牌營銷策略含答案
- 深度解析(2026)《GBT 18572-2001小艇 舷外機(jī)的靜推力測定》(2026年)深度解析
- 食品企業(yè)生產(chǎn)經(jīng)理面試寶典與答案解析
- 村會計筆試試題及答案
- 2026年江西省鐵路航空投資集團(tuán)校園招聘(24人)筆試考試參考題庫及答案解析
- 2025年徐州市教育局直屬學(xué)校招聘真題
- 消防設(shè)施共用責(zé)任劃分協(xié)議書范本
- 杜國楹小罐茶的創(chuàng)業(yè)講稿
- 2025-2026學(xué)年統(tǒng)編版九年級歷史上冊(全冊)知識點梳理歸納
- 滬教版(新版)一年級下學(xué)期數(shù)學(xué)第4單元100以內(nèi)的加減法單元試卷(附答案)
- 放射科CT檢查注意事項
- 物流運(yùn)輸服務(wù)方案投標(biāo)文件(技術(shù)方案)
- 產(chǎn)業(yè)園招商培訓(xùn)
- 2018版公路工程質(zhì)量檢驗評定標(biāo)準(zhǔn)分項工程質(zhì)量檢驗評定表路基土石方工程
評論
0/150
提交評論