關於執行緒池的工作佇列及新執行緒的流程順序

2022-09-16 20:57:18 字數 3406 閱讀 1105

new threadpoolexecutor(corepoolsize, maxpoolsize , keepalivetime ,timeunit, workqueue,threadfactory,rejectmethod )

新執行緒加入:

1. running 的執行緒 小於 corepoolsize ,直接建立新的執行緒在pool執行

2. running 的執行緒  等於corepoolsize ,則任務加入工作佇列

3.running 的執行緒  等於corepoolsize,工作佇列已滿,則加入 大於corepoolsize 小於 maxpoolsize 執行緒

4. 全部滿,執行拒絕策略

直接提交:工作佇列的預設選項是 synchronousqueue,它將任務直接提交給執行緒而不保持它們。在此,如果不存在可用於立即執行任務的執行緒,則試圖把任務加入佇列將失敗,因此會構造乙個新的執行緒。此策略可以避免在處理可能具有內部依賴性的請求集時出現鎖。直接提交通常要求無界maximumpoolsizes 以避免拒絕新提交的任務。當命令以超過佇列所能處理的平均數連續到達時,此策略允許無界線程具有增長的可能性。

無界佇列:使用無界佇列(例如,不具有預定義容量的 linkedblockingqueue)將導致在所有corepoolsize 執行緒都忙時新任務在佇列中等待。這樣,建立的執行緒就不會超過 corepoolsize。(因此,maximumpoolsize的值也就無效了。)當每個任務完全獨立於其他任務,即任務執行互不影響時,適合於使用無界佇列;例如,在 web頁伺服器中。這種排隊可用於處理瞬態突發請求,當命令以超過佇列所能處理的平均數連續到達時,此策略允許無界線程具有增長的可能性。

有界佇列:當使用有限的 maximumpoolsizes時,有界佇列(如 arrayblockingqueue)有助於防止資源耗盡,但是可能較難調整和控制。佇列大小和最大池大小可能需要相互折衷:使用大型佇列和小型池可以最大限度地降低 cpu 使用率、作業系統資源和上下文切換開銷,但是可能導致人工降低吞吐量。如果任務頻繁阻塞(例如,如果它們是 i/o邊界),則系統可能為超過您許可的更多執行緒安排時間。使用小型佇列通常要求較大的池大小,cpu使用率較高,但是可能遇到不可接受的排程開銷,這樣也會降低吞吐量。

例子一:使用直接提交策略,也即synchronousqueue

首先synchronousqueue是無界的,也就是說他存數任務的能力是沒有限制的,但是由於該queue本身的特性在某次新增元素後必須等待其他執行緒取走後才能繼續新增。在這裡不是核心執行緒便是新建立的執行緒,但是我們試想一樣下,下面的場景。

我們使用一下引數構造threadpoolexecutor

j**a**  

new threadpoolexecutor(2, 3, 30, timeunit.seconds, new synchronousqueue(), new recorderthreadfactory(  

"cookierecorderpool"), new threadpoolexecutor.callerrunspolicy());  

!!recorderthreadfactory(api沒有這個?!)

當核心執行緒已經有2個正在執行:

此時繼續來了乙個任務(a),根據前面介紹的「如果執行的執行緒等於或多於 corepoolsize,則executor始終首選將請求加入佇列,而不新增新的執行緒。」,所以a被新增到queue中。

又來了乙個任務(b),且核心2個執行緒還沒有忙完,ok,接下來首先嘗試1中描述,但是由於使用的synchronousqueue,所以一定無法加入進去。

此時便滿足了上面提到的「如果無法將請求加入佇列,則建立新的執行緒,除非建立此執行緒超出maximumpoolsize,在這種情況下,任務將被拒絕。」,所以必然會新建乙個執行緒來執行這個任務。

暫時還可以,但是如果這三個任務都還沒完成,連續來了兩個任務,第乙個新增入queue中,後乙個呢?queue中無法插入,而執行緒數達到了maximumpoolsize,所以只好執行異常策略了。

所以在使用synchronousqueue通常要求maximumpoolsize是無界的,這樣就可以避免上述情況發生(如果希望限制就直接使用有界佇列)。對於使用synchronousqueue的作用jdk中寫的很清楚:此策略可以避免在處理可能具有內部依賴性的請求集時出現鎖。

什麼意思?如果你的任務a1,a2有內部關聯,a1需要先執行,那麼先提交a1,再提交a2,當使用synchronousqueue我們可以保證,a1必定先被執行,在a1麼有被執行前,a2不可能新增入queue中。

例子二:使用無界佇列策略,即linkedblockingqueue

這個就拿newfixedthreadpool來說,根據前文提到的規則:

如果執行的執行緒少於 corepoolsize,則 executor 始終首選新增新的執行緒,而不進行排隊。那麼當任務繼續增加,會發生什麼呢?

如果執行的執行緒等於或多於 corepoolsize,則 executor 始終首選將請求加入佇列,而不新增新的執行緒。ok,此時任務變加入佇列之中了,那什麼時候才會新增新執行緒呢?

如果無法將請求加入佇列,則建立新的執行緒,除非建立此執行緒超出 maximumpoolsize,在這種情況下,任務將被拒絕。這裡就很有意思了,可能會出現無法加入佇列嗎?不像synchronousqueue那樣有其自身的特點,對於無界佇列來說,總是可以加入的(資源耗盡,當然另當別論)。換句說,永遠也不會觸發產生新的執行緒!corepoolsize大小的執行緒數會一直執行,忙完當前的,就從佇列中拿任務開始執行。所以要防止任務瘋長,比如任務執行的實行比較長,而新增任務的速度遠遠超過處理任務的時間,而且還不斷增加,不一會兒就爆了。

例子三:有界佇列,使用arrayblockingqueue

這個是最為複雜的使用,所以jdk不推薦使用也有些道理。與上面的相比,最大的特點便是可以防止資源耗盡的情況發生。

舉例來說,請看如下構造方法:

j**a**  

new threadpoolexecutor(2, 4, 30, timeunit.seconds, new arrayblockingqueue(2),  

new recorderthreadfactory("cookierecorderpool"), new threadpoolexecutor.callerrunspolicy());  

假設,所有的任務都永遠無法執行完。

對於首先來的a,b來說直接執行,接下來,如果來了c,d,他們會被放到queue中,如果接下來再來e,f,則增加執行緒執行e,f。但是如果再來任務,佇列無法再接受了,執行緒數也到達最大的限制了,所以就會使用拒絕策略來處理。

執行緒池的構造方法和執行緒池池工作佇列

執行緒池的構造方法 threadpoolexecutor int corepoolsize 核心執行緒數 int maximumpoolsize 最大執行緒數 long keepalivetime 空閒執行緒休眠時間 timeuint uint 時間單元 blockingqueueworkqueue...

執行緒工作佇列例項

include include include include include include include include include include include include include include include include include include includ...

執行緒池引數及佇列

引數名 含義corepoolsize 核心執行緒數 maximumpoolsize 最大執行緒數 keepalivetime 時間單位 空閒執行緒的存活時間 workqueue 用於存放任務的佇列 threadfactory 執行緒工廠 用來建立新執行緒 handler 處理別拒絕的任務 corep...