tomcat線程池和jdk線程池區(qū)別
概述
- 線程池是什么,為什么要線程池
- jdk有哪些線程池和原理
- 第三方中間件的線程池特點(diǎn)
- tomcat線程池
- quartz線程池
- 線程池展望
線程池是什么,為什么要線程池
我們?cè)陧?xiàng)目啟動(dòng)后,因?yàn)镃PU是多核的,為了充分利用CPU,可能需要啟動(dòng)多個(gè)線程去處理任務(wù)。頻繁的創(chuàng)建銷毀線程是有開銷的。所以需要?jiǎng)?chuàng)建線程后緩存住,讓線程可以頻繁的處理各種任務(wù)。這個(gè)時(shí)候就需要用線程池。線程池一般會(huì)緩存住N個(gè)線程,然后往線程池里面扔任務(wù)Runnable,線程不斷的處理任務(wù)。處理完了后停留在那里等待。
jdk有哪些線程池和原理
jdk線程池主要入口是在java.util.concurrent.Executors,其中包含
Executors.newFixedThreadPool 固定核心數(shù)的線程池,有多余任務(wù)往無(wú)界隊(duì)列中放
Executors.newSingleThreadExecutor 單線程的線程池,有多余任務(wù)往無(wú)界隊(duì)列中放
Executors.newCachedThreadPool 有任務(wù)來(lái)就開辟新的線程,不緩存任務(wù),不緩存線程。
Executors.newSingleThreadScheduledExecutor 單線程的延遲線程池,也就是說(shuō)可以指定任務(wù)N秒后周期性執(zhí)行。
Executors.newScheduledThreadPool N線程的延遲線程池,也就是說(shuō)可以指定任務(wù)N秒后周期性執(zhí)行。
* 這個(gè)線程池內(nèi)部依賴于ScheduledThreadPoolExecutor,隊(duì)列依賴于ScheduledThreadPoolExecutor.DelayedWorkQueue,這個(gè)隊(duì)列在take的時(shí)候,會(huì)根據(jù)你添加任務(wù)的時(shí)候的時(shí)間做一個(gè)awaitNanos(delay),代碼見該類的take方法。故而實(shí)現(xiàn)了延遲執(zhí)行。Executors.unconfigurableExecutorService
線程池的主要原理是在ThreadPoolExecutor這個(gè)類中。ThreadPoolExecutor有以下概念
corePoolSize
maximumPoolSize
keepAliveTime
workQueue
這四個(gè)概念一起解釋,corePoolSize指的是線程池核心數(shù),當(dāng)有任務(wù)進(jìn)來(lái)的時(shí)候,如果開辟的線程少于該值,就不斷開新線程去處理,直到該值。如果到了該值之后,就把新的任務(wù)存放在隊(duì)列中,也就是workQueue中,去存放的過(guò)程會(huì)成功和失敗,如果成功就存放在隊(duì)列中了,如果失敗,就繼續(xù)開辟線程去執(zhí)行。直到線程數(shù)等于maximumPoolSize,便不再開新線程。如果隊(duì)列滿了,線程也到最大值了,這個(gè)時(shí)候,就進(jìn)行拒絕策略。
AbortPolicy 默認(rèn)策略,拋出異常RejectedExecutionException。
DiscardPolicy 什么都不做
DiscardOldestPolicy 把任務(wù)頭丟掉,然后再把任務(wù)加入隊(duì)列
CallerRunsPolicy 主線程自己去執(zhí)行任務(wù)
RejectHandler 自己實(shí)現(xiàn)
原理大致說(shuō)明。ThreadPoolExecutor這個(gè)類除了參數(shù)那些參數(shù)外,還有HashSet<Worker> workers,參數(shù),該參數(shù)和線程一一對(duì)應(yīng),也就是說(shuō)線程池里面的線程就是worker,當(dāng)任務(wù)進(jìn)來(lái)后會(huì)和corePoolSize比較,繼而新建或者申請(qǐng)worker。新建的線程會(huì)不斷的從任務(wù)隊(duì)列去拿任務(wù),拿任務(wù)有超時(shí)時(shí)間,就是剛才說(shuō)到的keepAliveTime,到達(dá)時(shí)間后,如果說(shuō)當(dāng)前線程數(shù)大于corePollSize,則自己退出該線程。沒(méi)有大于則繼續(xù)循環(huán)運(yùn)行,繼續(xù)循環(huán)運(yùn)行就是在死循環(huán)中去拿隊(duì)列。
第三方中間件的線程池特點(diǎn)
tomcat線程池
tomcat的線程池是StandardThreadExecutor類,該類的屬性也是ThreadPoolExecutor,不過(guò)是把ThreadPoolExecutor動(dòng)態(tài)設(shè)置參數(shù)暴露出來(lái)了。所以StandardThreadExecutor類可以動(dòng)態(tài)的修改核心數(shù),最大數(shù)。并且該類的阻塞隊(duì)列使用的是繼承自LinkedBlockingQueue的TaskQueue。TaskQueue的核心在于,只要運(yùn)行當(dāng)前池子的線程小于maximumPoolSize,就拒絕你加入隊(duì)列。我們知道線程的數(shù)量和IO密集有關(guān)系,tomcat這種IO密集型的中間件,適合更多的線程。這里的線程數(shù)是maxThreads,該值默認(rèn)200,tomcat可以配置。
public boolean offer(Runnable o) {
//這個(gè)類是TaskQueue的的方法,我們知道,線程池,是先加入隊(duì)列,失敗后再開線程直到max數(shù)。
if (parent==null) return super.offer(o);
// 若運(yùn)行線程為最大線程數(shù),說(shuō)明線程已經(jīng)最大了,沒(méi)辦法,還是扔隊(duì)列吧。
if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
//若任務(wù)數(shù)小于線程數(shù),直接扔隊(duì)列,扔隊(duì)列后,那些閑著的線程自然會(huì)取。沒(méi)必要返回false,讓其再重開線程。
if (parent.getSubmittedCount()<(parent.getPoolSize())) return super.offer(o);
// 發(fā)現(xiàn)運(yùn)行線程數(shù)少于池最大線程數(shù),拒絕加入隊(duì)列,讓線程池繼續(xù)開線程。
if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
//if we reached here, we need to add it to the queue
return super.offer(o);
}
quartz線程池
quartz線程池是在org.quartz.simpl.SimpleThreadPool
該線程池非常簡(jiǎn)單,初始化的時(shí)候新建N個(gè)線程。這個(gè)線程是在線程池初始化的時(shí)候就新建。然后把線程放在availWorkers中,如果有任務(wù)進(jìn)來(lái),就從availWorkers拿出1個(gè)線程放進(jìn)busyWorkers,然后把任務(wù)給該線程執(zhí)行。執(zhí)行完了之后返回給availWorkers中。如果線程滿了,主線程扔任務(wù)的時(shí)候,會(huì)檢查availWorkers的size,當(dāng)size<1的時(shí)候,停半秒繼續(xù)檢查availWorkers直到有可用才運(yùn)行任務(wù)。