執行緒池 如何正確的配置乙個執行緒池

2022-09-19 20:06:13 字數 2399 閱讀 9150

我們在建立自己的執行緒池時,會時常因為不知道給核心執行緒數或者最大執行緒數設定多少為好,其實這個時需要看你的執行緒池的使用場景和伺服器cup的配置,根據這些前置條件,我們再去判斷如何去設定合適的執行緒數,並不是我們想設定多少執行緒數大小就可以設定多少,這樣可能會導致執行緒發揮不到最大的效能,甚至還有可能會導致服務oom堆疊溢位的風險。

當我們遇到那種需要大量使用cup的任務時,比如加密、解密、壓縮、計算等一系列操作,這種情況理想的執行緒核心數是cpu核心數的1~2倍,不宜設定的太多。如果設定太多的執行緒數,當執行緒任務越來越多的時候,導致cup的執行緒的壓力會越來越大並且執行緒的數量也會越來越多,導致cpu去切換執行緒上下文的時間成本會越來越多,這樣不僅不會提公升執行緒的效能,可能還會導致執行緒的的效能越來越低。

耗時io其實就是不太耗cup的資源,只是會在執行上會有一定的等待時間,比如運算元據庫,讀取大檔案,或者請求介面等。當我們從資料庫查詢乙個很大的資料時,需要等待很久的時間,其實這個等待的時間他並不消耗cpu的資源。所以在這種場景上,我們設定的最大執行緒數其實可以大於核心執行緒數很多倍。因為他大部分執行緒都不怎麼消耗執行緒資源。

那麼場景有了,我該如何去獲取我們可以設定的核心執行緒數的值呢?

可以看到下面這個公式

可以看到

公式有了,但是又缺少執行緒的時間,執行時間和等待時間我該從哪去獲取呢?那麼這個就需要我們去借用一些工具了,想要獲取到準確的時間,就需要進行壓測,然後監控jvm的執行緒情況以及cpu的負載情況。最後得到他們的時間,然後合理充分的利用資源。

了解了執行緒場景後,我們再來看一下,如何通過我們需要的應用場景來設定對於的執行緒池初始引數。

上面我們說到過,設定合理的執行緒數,需要看執行緒的使用場景是cpu密集型任務還是耗時io型任務。

核心執行緒數的設定量就按照上面的公式來計算

那麼有沒有通用的設定方法,就是可以適用於cpu密集型任務和耗時io型任務都適用的設定方法。如果想設定這樣的執行緒,核心執行緒數不變,但是最大執行緒數可以設定的稍微大一點,一般為核心執行緒數的3~4倍。這種是常用方法,只有在你沒有對執行緒效能進行壓測的情況下進行設定,如果想獲取最準確的,還是以壓測後的執行緒狀態,依情況來合理設定。

之前我們有介紹過linkedblockingqueue,synchronousqueue,delayedworkqueue這三種佇列,具體可以參考

常用的三種阻塞佇列這篇文章,這三種佇列其實都有乙個缺點,那就是不好控制線程的的最大數量,這樣可能會導致出現因為佇列中的執行緒任務太多導致記憶體爆滿爆出oom異常。為了避免這種情況,我們再平時設定自己的執行緒時,都回去使用arrayblockingqueue這個阻塞佇列,這個阻塞佇列唯一的好處就是,他可以設定佇列的大小,並且佇列滿了之後,他也不會將塞不進來的執行緒任務拋棄,而是會阻塞著,然後根據執行緒池設定的拒絕策略來執行,這樣也就避免了丟失執行緒任務的風險。那麼根據使用場景,我們該如何去設定佇列的容量呢?如果我們使用容量更大的佇列和更小的執行緒數,就可以減少上下文切換帶來的開銷,但也可能會因此降低整體的吞吐量,如果我們的任務是io密集型,則可以選擇稍小容量的佇列和更大的最大執行緒數,這樣整體的執行緒效率就會高,不過也會帶來更大的上下文切換。我們知道了這個東西之後,其實會發現設定多大的佇列大小,並沒有乙個準確的公式,而是慢慢的業務執行的時候慢慢的試錯,當發現我們的容量設定的並不理想,則需要依照當時的情況來調整。

執行緒工廠的作用其實就是建立執行緒,如果你對執行緒池的預設建立執行緒的方式不滿意,想要對執行緒池裡面的執行緒名字修改,或者在建立執行緒的時候去執行一些前置任務,這個時候你就可以自己建立乙個執行緒工廠,示例**如下。

public class testthreadfactory implements threadfactory 

}

拒絕策略目前有四種,abortpolicy,discardpolicy,discardoldestpolicy 或者 callerrunspolicy。詳細的作用我們就不說了,可以移步到執行緒池的4種拒絕策略這篇文章中檢視,按照自己的需求來使用合適的拒絕策略,當然,如果你不滿足這四種執行緒策略,想要自己去整活,執行緒池也會給你機會,這時你就可以通過實現rejectedexecutionhandler類來實現自己的拒絕策略,示例**如下。

public class myrejectpolicy implements rejectedexecutionhandler 

}

說了這麼多,大家可能會發現,其實執行緒池的建立與配置,並沒有乙個統一的標準,文章中給出的一些公式,其實僅僅供於在第一次建立執行緒池的時候的配置,如果到後面發現,當先建立的執行緒規則不滿足於現狀,其實這個時候你就可以以自己看到的狀況來定,比如執行緒執行的時間過長,並且很占用時間,但又不消耗cpu資源,這個時候其實你就會覺得我可以吧最大執行緒池擴大一點,相反遇到執行緒執行很慢,並且同時執行的執行緒又很多,這個時候可能就是遇到了cpu密集執行的情況,這時就可以將最大執行緒數調小一點,執行緒的佇列容量再調大一點。這樣下來,可能就會有不一樣的效果。

執行緒池 如何正確的關閉執行緒池

在呼叫這個關閉方法時,執行緒池會根據我們配置的拒絕策略來拒絕掉想要進來的執行緒,也就是說吧建立執行執行緒的入口給關閉掉了,直到執行緒池內的所有執行緒都執行完成。在呼叫這個方法完畢之後,並不代表這個執行緒池就真的都停掉了,只能說他不讓其他執行緒進來了,然後等到執行緒池內的執行緒執行完。shutdown...

執行緒池(一) 實現乙個簡單的執行緒池

我們知道頻繁的建立 銷毀執行緒是不可取的,為了減少建立和銷毀執行緒的次數,讓每個執行緒可以多次使用,我們就可以使用執行緒池,可以降低資源到的消耗。執行緒池裡面肯定有多個執行緒,那麼我們就簡單的用乙個陣列來儲存執行緒,那我們我們預設裡面有 5 個執行緒。那我們執行緒池裡只有五個執行緒能同時工作,那同時...

正確關閉執行緒池

使用10個固定執行緒池建立100個任務 executorservice service executors.newfixedthreadpool 10 for int i 0 i 100 i threadpoolexecutor中關閉執行緒池的方法 shutdown 可以安全地關閉乙個執行緒池,呼叫...