溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Tomcat怎么擴(kuò)展線程池

發(fā)布時(shí)間:2021-12-24 17:37:57 來源:億速云 閱讀:110 作者:iii 欄目:開發(fā)技術(shù)

本篇內(nèi)容主要講解“Tomcat怎么擴(kuò)展線程池”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“Tomcat怎么擴(kuò)展線程池”吧!

JDK 線程池

通常我們可以將執(zhí)行的任務(wù)分為兩類:

  • cpu 密集型任務(wù)

  • io 密集型任務(wù)

cpu 密集型任務(wù),需要線程長(zhǎng)時(shí)間進(jìn)行的復(fù)雜的運(yùn)算,這種類型的任務(wù)需要少創(chuàng)建線程,過多的線程將會(huì)頻繁引起上文切換,降低任務(wù)處理處理速度。

而 io 密集型任務(wù),由于線程并不是一直在運(yùn)行,可能大部分時(shí)間在等待 IO 讀取/寫入數(shù)據(jù),增加線程數(shù)量可以提高并發(fā)度,盡可能多處理任務(wù)。

JDK 原生線程池工作流程如下:

Tomcat怎么擴(kuò)展線程池

線程池執(zhí)行流程圖

“詳情可以查看 一文教你安全的關(guān)閉線程池, 上圖假設(shè)使用 LinkedBlockingQueue。

“靈魂拷問:上述流程是否記錯(cuò)過?在很長(zhǎng)一段時(shí)間內(nèi),我都認(rèn)為線程數(shù)量到達(dá)最大線程數(shù),才放入隊(duì)列中。 ̄□ ̄||

上圖中可以發(fā)現(xiàn)只要線程池線程數(shù)量大于核心線程數(shù),就會(huì)先將任務(wù)加入到任務(wù)隊(duì)列中,只有任務(wù)隊(duì)列加入失敗,才會(huì)再新建線程。也就是說原生線程池隊(duì)列未滿之前,最多只有核心線程數(shù)量線程。

這種策略顯然比較適合處理 cpu 密集型任務(wù),但是對(duì)于 io 密集型任務(wù),如數(shù)據(jù)庫查詢,rpc 請(qǐng)求調(diào)用等,就不是很友好了。

由于 Tomcat/Jetty  需要處理大量客戶端請(qǐng)求任務(wù),如果采用原生線程池,一旦接受請(qǐng)求數(shù)量大于線程池核心線程數(shù),這些請(qǐng)求就會(huì)被放入到隊(duì)列中,等待核心線程處理。這樣做顯然降低這些請(qǐng)求總體處理速度,所以兩者都沒采用  JDK 原生線程池。

解決上面的辦法可以像 Jetty 自己實(shí)現(xiàn)線程池組件,這樣就可以更加適配內(nèi)部邏輯,不過開發(fā)難度比較大,另一種就像 Tomcat 一樣,擴(kuò)展原生 JDK  線程池,實(shí)現(xiàn)比較簡(jiǎn)單。

下面主要以 Tomcat 擴(kuò)展線程池,講講其實(shí)現(xiàn)原理。

擴(kuò)展線程池

首先我們從 JDK 線程池源碼出發(fā),查看如何這個(gè)基礎(chǔ)上擴(kuò)展。

Tomcat怎么擴(kuò)展線程池

可以看到線程池流程主要分為三步,第二步根據(jù) queue#offer 方法返回結(jié)果,判斷是否需要新建線程。

JDK 原生隊(duì)列類型 LinkedBlockingQueue, SynchronousQueue,兩者實(shí)現(xiàn)邏輯不盡相同。

LinkedBlockingQueue

offer 方法內(nèi)部將會(huì)根據(jù)隊(duì)列是否已滿作為判斷條件。若隊(duì)列已滿,返回 false,若隊(duì)列未滿,則將任務(wù)加入隊(duì)列中,且返回 true。

SynchronousQueue

這個(gè)隊(duì)列比較特殊,內(nèi)部不會(huì)儲(chǔ)存任何數(shù)據(jù)。若有線程將任務(wù)放入其中將會(huì)被阻塞,直到其他線程將任務(wù)取出。反之,若無其他線程將任務(wù)放入其中,該隊(duì)列取任務(wù)的方法也將會(huì)被阻塞,直到其他線程將任務(wù)放入。

對(duì)于 offer 方法來說,若有其他線程正在被取方法阻塞,該方法將會(huì)返回 true。反之,offer 方法將會(huì)返回 false。

所以若想實(shí)現(xiàn)適合 io 密集型任務(wù)線程池,即優(yōu)先新建線程處理任務(wù),關(guān)鍵在于 queue#offer  方法。可以重寫該方法內(nèi)部邏輯,只要當(dāng)前線程池?cái)?shù)量小于最大線程數(shù),該方法返回false,線程池新建線程處理。

當(dāng)然上述實(shí)現(xiàn)邏輯比較糙,下面我們就從 Tomcat 源碼查看其實(shí)現(xiàn)邏輯。

Tomcat 擴(kuò)展線程池

Tomcat 擴(kuò)展線程池直接繼承 JDK 線程池  java.util.concurrent.ThreadPoolExecutor,重寫部分方法的邏輯。另外還實(shí)現(xiàn)了 TaskQueue,直接繼承  LinkedBlockingQueue,重寫 offer 方法。

首先查看 Tomcat 線程池的使用方法。

Tomcat怎么擴(kuò)展線程池

可以看到 Tomcat 線程池使用方法與普通的線程池差不太多。

接著我們查看一下 Tomcat 線程池核心方法 execute 的邏輯。

Tomcat怎么擴(kuò)展線程池

execute 方法邏輯比較簡(jiǎn)單,任務(wù)核心還是交給 Java 原生線程池處理。這里主要增加一個(gè)重試策略,如果原生線程池執(zhí)行拒絕策略的情況,拋出  RejectedExecutionException 異常。這里將會(huì)捕獲,然后重新再次嘗試將任務(wù)加入到 TaskQueue ,盡最大可能執(zhí)行任務(wù)。

這里需要注意 submittedCount 變量。這是 Tomcat 線程池內(nèi)部一個(gè)重要的參數(shù),它是一個(gè) AtomicInteger  變量,將會(huì)實(shí)時(shí)統(tǒng)計(jì)已經(jīng)提交到線程池中,但還沒有執(zhí)行結(jié)束的任務(wù)。也就是說 submittedCount  等于線程池隊(duì)列中的任務(wù)數(shù)加上線程池工作線程正在執(zhí)行的任務(wù)。TaskQueue#offer 將會(huì)使用該參數(shù)實(shí)現(xiàn)相應(yīng)的邏輯。

接著我們主要查看 TaskQueue#offer 方法邏輯。

Tomcat怎么擴(kuò)展線程池

核心邏輯在于第三步,這里如果 submittedCount 小于當(dāng)前線程池線程數(shù)量,將會(huì)返回false。上面我們講到 offer 方法返回  false,線程池將會(huì)直接創(chuàng)建新線程。

Dubbo 2.6.X 版本增加 EagerThreadPool,其實(shí)現(xiàn)原理與 Tomcat 線程池差不多,感興趣的小伙伴可以自行翻閱。

折衷方法

上述擴(kuò)展方法雖然看起不是很難,但是自己實(shí)現(xiàn)代價(jià)可能就比較大。若不想擴(kuò)展線程池運(yùn)行 io 密集型任務(wù),可以采用下面這種折衷方法。

new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS, new  LinkedBlockingQueue(100));

不過使用這種方式將會(huì)使 keepAliveTime 失效,線程一旦被創(chuàng)建,將會(huì)一直存在,比較浪費(fèi)系統(tǒng)資源。

到此,相信大家對(duì)“Tomcat怎么擴(kuò)展線程池”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

向AI問一下細(xì)節(jié)
推薦閱讀:
  1. Tomcat
  2. ThreadPool:線程池

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI