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