Tomcat中的线程池(APR和ThreadPool)
一、容器簡(jiǎn)化了程序員自身的多線程編程。
??????? 各種Web容器,如Tomcat,Resion,Jetty等都有自己的線程池(可在配置文件中配置),所以在客戶端進(jìn)行請(qǐng)求調(diào)用的時(shí)候,程序員不用針對(duì)Client的每一次請(qǐng)求,都新建一個(gè)線程。而容器會(huì)自動(dòng)分配線程池中的線程,提高訪問(wèn)速度。
?
二、Tomcat線程池實(shí)現(xiàn):
1、使用APR的Pool技術(shù),使用了JNI。
Tomcat從5.5.17開(kāi)始,為了提高響應(yīng)速度和效率,使用了Apache Portable Runtime(APR)作為最底層,使用了APR中包含Socket、緩沖池等多種技術(shù),性能也提高了。APR也是Apache HTTPD的最底層。
?
2、使用Java實(shí)現(xiàn)的Thread Pool。
????? ThreadPool默認(rèn)創(chuàng)建了5個(gè)線程,保存在一個(gè)200維的線程數(shù)組中,創(chuàng)建時(shí)就啟動(dòng)了這些線程,當(dāng)然在沒(méi)有請(qǐng)求時(shí),它們都處理“等待”狀態(tài)(其實(shí)就是一個(gè)while循環(huán),不停的等待notify)。如果有請(qǐng)求時(shí),空閑線程會(huì)被喚醒執(zhí)行用戶的請(qǐng)求。
????? 具體的請(qǐng)求過(guò)程是: 服務(wù)啟動(dòng)時(shí),創(chuàng)建一個(gè)一維線程數(shù)組(maxThread=200個(gè)),并創(chuàng)建空閑線程(minSpareThreads=5個(gè))隨時(shí)等待用戶請(qǐng)求。 當(dāng)有用戶請(qǐng)求時(shí),調(diào)用 threadpool.runIt(ThreadPoolRunnable)方法,將一個(gè)需要執(zhí)行的實(shí)例傳給ThreadPool中。其中用戶需要執(zhí)行的 實(shí)例必須實(shí)現(xiàn)ThreadPoolRunnable接口。 ThreadPool 首先查找空閑的線程,如果有則用它運(yùn)行要執(zhí)行的ThreadPoolRunnable;
????? 如果沒(méi)有空閑線程并且沒(méi)有超過(guò)maxThreads,就一次性創(chuàng)建 minSpareThreads個(gè)空閑線程;如果已經(jīng)超過(guò)了maxThreads了,就等待空閑線程了。總之,要找到空閑的線程,以便用它執(zhí)行實(shí)例。找到后,將該線程從線程數(shù)組中移走。 接著喚醒已經(jīng)找到的空閑線程,用它運(yùn)行執(zhí)行實(shí)例(ThreadPoolRunnable)。 運(yùn)行完ThreadPoolRunnable后,就將該線程重新放到線程數(shù)組中,作為空閑線程供后續(xù)使用。
??????? 由此可以看出,Tomcat的線程池實(shí)現(xiàn)是比較簡(jiǎn)單的,ThreadPool.java也只有840行代碼。用一個(gè)一維數(shù)組保存空閑的線程,每次以一個(gè)較小步伐(5個(gè))創(chuàng)建空閑線程并放到線程池中。使用時(shí)從數(shù)組中移走空閑的線程,用完后,再“歸還”給線程池。
??????
總結(jié):?
???????? tomcat5.5.10以上版本開(kāi)始支持apr,支持通過(guò)apache runtime module進(jìn)行JNI調(diào)用,使用本地代碼來(lái)加速網(wǎng)絡(luò)處理。
?????? 如果不使用apr之前,Tomcat的Servlet線程池使用的是阻塞IO的模式,使用apr之后,線程池變成了 NIO的非阻塞模式,而且這種NIO還是使用了操作系統(tǒng)的本地代碼,看tomcat文檔上面的說(shuō)法是,極大提升web處理能力,不再需要專門放一個(gè)web server處理靜態(tài)頁(yè)面了。?
?????? 我自己直觀的感受是,不用apr之前,你配置多少個(gè)等待線程,tomcat就會(huì)啟動(dòng)多少個(gè)線程掛起等待,使用apr以后,不管你配置多少,就只有幾個(gè)NIO調(diào)度的線程,這一點(diǎn)你可以通過(guò)kill -3 PID,然后察看log得知。
?????? 假設(shè)不使用apr,可能端口的線程調(diào)度能力比較差,所以通過(guò)iptables進(jìn)行端口轉(zhuǎn)發(fā),讓兩個(gè)端口去分擔(dān)一個(gè)端口的線程調(diào)度,就有可能減少線程調(diào)度的并發(fā),從而提高處理能力,減少資源消耗。
?
三、配置Tomcat線程池以使用高并發(fā)連接
1.打開(kāi)共享的線程池:
<Service name="Catalina">??
? <!--The connectors can use a shared executor, you can define one or more named thread pools-->??
??
??? <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"????
??? maxThreads="1000" minSpareThreads="50" maxIdleTime="600000"/>
默認(rèn)前后是注釋<!-- -->掉的,去掉就可以了。
重要參數(shù)說(shuō)明:
name:共享線程池的名字。這是Connector為了共享線程池要引用的名字,該名字必須唯一。默認(rèn)值:None;
namePrefix:在JVM上,每個(gè)運(yùn)行線程都可以有一個(gè)name 字符串。這一屬性為線程池中每個(gè)線程的name字符串設(shè)置了一個(gè)前綴,Tomcat將把線程號(hào)追加到這一前綴的后面。默認(rèn)值:tomcat-exec-;
maxThreads:該線程池可以容納的最大線程數(shù)。默認(rèn)值:200;
maxIdleTime:在Tomcat關(guān)閉一個(gè)空閑線程之前,允許空閑線程持續(xù)的時(shí)間(以毫秒為單位)。只有當(dāng)前活躍的線程數(shù)大于minSpareThread的值,才會(huì)關(guān)閉空閑線程。默認(rèn)值:60000(一分鐘)。
minSpareThreads:Tomcat應(yīng)該始終打開(kāi)的最小不活躍線程數(shù)。默認(rèn)值:25。
threadPriority:線程的等級(jí)。默認(rèn)是Thread.NORM_PRIORITY
?
2. 在Connector中指定使用共享線程池:
<Connector executor="tomcatThreadPool"
??? ?????? port="8080" protocol="HTTP/1.1"
?????????????? connectionTimeout="20000"
?????????????? redirectPort="8443"?
??? ?????? minProcessors="5"
??? ?????? maxProcessors="75"
??? ?????? acceptCount="1000"/>
?
重要參數(shù)說(shuō)明:
executor:表示使用該參數(shù)值對(duì)應(yīng)的線程池;
minProcessors:服務(wù)器啟動(dòng)時(shí)創(chuàng)建的處理請(qǐng)求的線程數(shù);
maxProcessors:最大可以創(chuàng)建的處理請(qǐng)求的線程數(shù);
acceptCount:指定當(dāng)所有可以使用的處理請(qǐng)求的線程數(shù)都被使用時(shí),可以放到處理隊(duì)列中的請(qǐng)求數(shù),超過(guò)這個(gè)數(shù)的請(qǐng)求將不予處理。
?
BTW:我測(cè)試了一下,由于每次請(qǐng)求不再需要重新分配線程,系統(tǒng)響應(yīng)速度還是有很明顯的改善的。
轉(zhuǎn)自:http://josh-persistence.iteye.com/blog/1973612
總結(jié)
以上是生活随笔為你收集整理的Tomcat中的线程池(APR和ThreadPool)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 大数据在银行业的应用场景
- 下一篇: 从JVM的常见异常来看Tomcat中内存