欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java線程池不同場景下使用示例經(jīng)驗總結(jié)

 更新時間:2022年03月11日 11:30:21   作者:Q.E.D.  
這篇文章主要為大家介紹了java線程池不同場景如何使用的示例源碼及經(jīng)驗總結(jié),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步

引導(dǎo)語

ThreadPoolExecutor 初始化時,主要有如下幾個參數(shù):

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {

大家對這幾個參數(shù)應(yīng)該都很熟悉了,雖然參數(shù)很少,但實際工作中卻有很多門道,大多數(shù)的問題主要集中在線程大小的設(shè)置,隊列大小的設(shè)置兩方面上,接下來我們一起看看工作中,如何初始化 ThreadPoolExecutor。

1、coreSize == maxSize

我相信很多人都看過,或自己寫過這樣的代碼:

ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 600000L, TimeUnit.DAYS,
                                                     new LinkedBlockingQueue());

這行代碼主要展示了在初始化 ThreadPoolExecutor 的時候,coreSize 和 maxSize 是相等的,這樣設(shè)置的話,隨著請求的不斷增加,會是這樣的現(xiàn)象:

  • 請求數(shù) < coreSize 時,新增線程;
  • 請求數(shù) >= coreSize && 隊列不滿時,添加任務(wù)入隊;
  • 隊列滿時,此時因為 coreSize 和 maxSize 相等,任務(wù)會被直接拒絕。

這么寫的最大目的:是想讓線程一下子增加到 maxSize,并且不要回收線程,防止線程回收,避免不斷增加回收的損耗,一般來說業(yè)務(wù)流量都有波峰低谷,在流量低谷時,線程不會被回收;流量波峰時,maxSize 的線程可以應(yīng)對波峰,不需要慢慢初始化到 maxSize 的過程。

這樣設(shè)置有兩個前提條件:

allowCoreThreadTimeOut 我們采取默認(rèn) false,而不會主動設(shè)置成 true,allowCoreThreadTimeOut 是 false 的話,當(dāng)線程空閑時,就不會回收核心線程;

keepAliveTime 和 TimeUnit 我們都會設(shè)置很大,這樣線程空閑的時間就很長,線程就不會輕易的被回收。

我們現(xiàn)在機(jī)器的資源都是很充足的,我們不用去擔(dān)心線程空閑會浪費(fèi)機(jī)器的資源,所以這種寫法目前是很常見的。

2、maxSize 無界 + SynchronousQueue

在線程池選擇隊列時,我們也會看到有同學(xué)選擇 SynchronousQueue,SynchronousQueue 我們在 《SynchronousQueue 源碼解析》章節(jié)有說過,其內(nèi)部有堆棧和隊列兩種形式,默認(rèn)是堆棧的形式,其內(nèi)部是沒有存儲的容器的,放元素和拿元素是一一對應(yīng)的,比如我使用 put 方法放元素,如果此時沒有對應(yīng)的 take 操作的話,put 操作就會阻塞,需要有線程過來執(zhí)行 take 操作后,put 操作才會返回。

基于此特點(diǎn),如果要使用 SynchronousQueue 的話,我們需要盡量將 maxSize 設(shè)置大一點(diǎn),這樣就可以接受更多的請求。

假設(shè)我們設(shè)置 maxSize 是 10 的話,選擇 SynchronousQueue 隊列,假設(shè)所有請求都執(zhí)行 put 操作,沒有請求執(zhí)行 take 操作,前 10 個 put 請求會消耗 10 個線程,都阻塞在 put 操作上,第 11 個請求過來后,請求就會被拒絕,所以我們才說盡量把 maxSize 設(shè)置大一點(diǎn),防止請求被拒絕。

maxSize 無界 + SynchronousQueue 這樣的組合方式優(yōu)缺點(diǎn)都很明顯:

優(yōu)點(diǎn):

當(dāng)任務(wù)被消費(fèi)時,才會返回,這樣請求就能夠知道當(dāng)前請求是已經(jīng)在被消費(fèi)了,如果是其他的隊列的話,我們只知道任務(wù)已經(jīng)被提交成功了,但無法知道當(dāng)前任務(wù)是在被消費(fèi)中,還是正在隊列中堆積。

缺點(diǎn):

比較消耗資源,大量請求到來時,我們會新建大量的線程來處理請求;

如果請求的量難以預(yù)估的話,maxSize 的大小也很難設(shè)置。

3、maxSize 有界 + Queue 無界

在一些對實時性要求不大,但流量忽高忽低的場景下,可以使用 maxSize 有界 + Queue 無界的組合方式。

比如我們設(shè)置 maxSize 為 20,Queue 選擇默認(rèn)構(gòu)造器的 LinkedBlockingQueue,這樣做的優(yōu)缺點(diǎn)如下:

優(yōu)點(diǎn):

電腦 cpu 固定的情況下,每秒能同時工作的線程數(shù)是有限的,此時開很多的線程其實也是浪費(fèi),還不如把這些請求放到隊列中去等待,這樣可以減少線程之間的 CPU 的競爭;

LinkedBlockingQueue 默認(rèn)構(gòu)造器構(gòu)造出來的鏈表的最大容量是 Integer 的最大值,非常適合流量忽高忽低的場景,當(dāng)流量高峰時,大量的請求被阻塞在隊列中,讓有限的線程可以慢慢消費(fèi)。

缺點(diǎn):

流量高峰時,大量的請求被阻塞在隊列中,對于請求的實時性難以保證,所以當(dāng)對請求的實時性要求較高的場景,不能使用該組合。

4、maxSize 有界 + Queue 有界

這種組合是對 3 缺點(diǎn)的補(bǔ)充,我們把隊列從無界修改成有界,只要排隊的任務(wù)在要求的時間內(nèi),能夠完成任務(wù)即可。

這種組合需要我們把線程和隊列的大小進(jìn)行配合計算,保證大多數(shù)請求都可以在要求的時間內(nèi),有響應(yīng)返回。

5、keepAliveTime 設(shè)置無窮大

有些場景下我們不想讓空閑的線程被回收,于是就把 keepAliveTime 設(shè)置成 0,實際上這種設(shè)置是錯誤的,當(dāng)我們把 keepAliveTime 設(shè)置成 0 時,線程使用 poll 方法在隊列上進(jìn)行超時阻塞時,會立馬返回 null,也就是空閑線程會立馬被回收。

所以如果我們想要空閑的線程不被回收,我們可以設(shè)置 keepAliveTime 為無窮大值,并且設(shè)置 TimeUnit 為時間的大單位,比如我們設(shè)置 keepAliveTime 為 365,TimeUnit 為 TimeUnit.DAYS,意思是線程空閑 1 年內(nèi)都不會被回收。

在實際的工作中,機(jī)器的內(nèi)存一般都夠大,我們合理設(shè)置 maxSize 后,即使線程空閑,我們也不希望線程被回收,我們常常也會設(shè)置 keepAliveTime 為無窮大。

6、線程池的公用和獨(dú)立

在實際工作中,某一個業(yè)務(wù)下的所有場景,我們都不會公用一個線程池,一般有以下幾個原則:

查詢和寫入不公用線程池,互聯(lián)網(wǎng)應(yīng)用一般來說,查詢量遠(yuǎn)遠(yuǎn)大于寫入的量,如果查詢和寫入都要走線程池的話,我們一定不要公用線程池,也就是說查詢走查詢的線程池,寫入走寫入的線程池,如果公用的話,當(dāng)查詢量很大時,寫入的請求可能會到隊列中去排隊,無法及時被處理;

多個寫入業(yè)務(wù)場景看情況是否需要公用線程池,原則上來說,每個業(yè)務(wù)場景都獨(dú)自使用自己的線程池,絕不共用,這樣在業(yè)務(wù)治理、限流、熔斷方面都比較容易,一旦多個業(yè)務(wù)場景公用線程池,可能就會造成業(yè)務(wù)場景之間的互相影響,現(xiàn)在的機(jī)器內(nèi)存都很大,每個寫入業(yè)務(wù)場景獨(dú)立使用自己的線程池也是比較合理的;

多個查詢業(yè)務(wù)場景是可以公用線程池的,查詢的請求一般來說有幾個特點(diǎn):查詢的場景多、rt 時間短、查詢的量比較大,如果給每個查詢場景都弄一個單獨(dú)的線程池的話,第一個比較耗資源,第二個很難定義線程池中線程和隊列的大小,比較復(fù)雜,所以多個相似的查詢業(yè)務(wù)場景是可以公用線程池的。

7、如何算線程大小和隊列大小

在實際的工作中,我們使用線程池時,需要慎重考慮線程的大小和隊列的大小,主要從幾個方面入手:

  • 根據(jù)業(yè)務(wù)進(jìn)行考慮,初始化線程池時,我們需要考慮所有業(yè)務(wù)涉及的線程池,如果目前所有的業(yè)務(wù)同時都有很大流量,那么在對于當(dāng)前業(yè)務(wù)設(shè)置線程池時,我們盡量把線程大小、隊列大小都設(shè)置小,如果所有業(yè)務(wù)基本上都不會同時有流量,那么就可以稍微設(shè)置大一點(diǎn);
  • 根據(jù)業(yè)務(wù)的實時性要求,如果實時性要求高的話,我們把隊列設(shè)置小一點(diǎn),coreSize == maxSize,并且設(shè)置 maxSize 大一點(diǎn),如果實時性要求低的話,就可以把隊列設(shè)置大一點(diǎn)。

假設(shè)現(xiàn)在機(jī)器上某一時間段只會運(yùn)行一種業(yè)務(wù),業(yè)務(wù)的實時性要求較高,每個請求的平均 rt 是 200ms,請求超時時間是 2000ms,機(jī)器是 4 核 CPU,內(nèi)存 16G,一臺機(jī)器的 qps 是 100,這時候我們可以模擬一下如何設(shè)置:

4 核 CPU,假設(shè) CPU 能夠跑滿,每個請求的 rt 是 200ms,就是 200 ms 能執(zhí)行 4 條請求,2000ms 內(nèi)能執(zhí)行 2000/200 * 4 = 40 條請求;

200 ms 能執(zhí)行 4 條請求,實際上 4 核 CPU 的性能遠(yuǎn)遠(yuǎn)高于這個,我們可以拍腦袋加 10 條,也就是說 2000ms 內(nèi)預(yù)估能夠執(zhí)行 50 條;

一臺機(jī)器的 qps 是 100,此時我們計算一臺機(jī)器 2 秒內(nèi)最多處理 50 條請求,所以此時如果不進(jìn)行 rt 優(yōu)化的話,我們需要加至少一臺機(jī)器。

線程池可以大概這么設(shè)置:

ThreadPoolExecutor executor = new ThreadPoolExecutor(15, 15, 365L, TimeUnit.DAYS,
                                                     new LinkedBlockingQueue(35));

線程數(shù)最大為 15,隊列最大為 35,這樣機(jī)器差不多可以在 2000ms 內(nèi)處理最大的請求 50 條,當(dāng)然根據(jù)你機(jī)器的性能和實時性要求,你可以調(diào)整線程數(shù)和隊列的大小占比,只要總和小于 50 即可。

以上只是很粗糙的設(shè)置,在實際的工作中,還需要根據(jù)實際情況不斷的觀察和調(diào)整。

8、總結(jié)

線程池設(shè)置非常重要,我們盡量少用 Executors 類提供的各種初始化線程池的方法,多根據(jù)業(yè)務(wù)的量,實時性要求來計算機(jī)器的預(yù)估承載能力,設(shè)置預(yù)估的線程和隊列大小,并且根據(jù)實時請求不斷的調(diào)整線程池的大小值。

以上就是java線程池不同場景下使用示例經(jīng)驗總結(jié)的詳細(xì)內(nèi)容,更多關(guān)于java線程池不同場景使用經(jīng)驗的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 基于Jmeter生成測試報告過程圖解

    基于Jmeter生成測試報告過程圖解

    這篇文章主要介紹了基于Jmeter生成測試報告過程圖解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2020-08-08
  • IDEA項目中配置Maven鏡像源(下載源)的詳細(xì)過程

    IDEA項目中配置Maven鏡像源(下載源)的詳細(xì)過程

    Maven是一個能使我們的java程序開發(fā)節(jié)省時間和精力,是開發(fā)變得相對簡單,還能使開發(fā)規(guī)范化的工具,下面這篇文章主要給大家介紹了關(guān)于IDEA項目中配置Maven鏡像源(下載源)的詳細(xì)過程,需要的朋友可以參考下
    2024-02-02
  • 淺談springboot一個service內(nèi)組件的加載順序

    淺談springboot一個service內(nèi)組件的加載順序

    這篇文章主要介紹了springboot一個service內(nèi)組件的加載順序,具有很好的參考價值,希望對大家有所幫助。以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家
    2021-08-08
  • Druid之連接創(chuàng)建及銷毀示例詳解

    Druid之連接創(chuàng)建及銷毀示例詳解

    這篇文章主要為大家介紹了Druid之連接創(chuàng)建及銷毀示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-02-02
  • Maven實現(xiàn)自己的starter依賴

    Maven實現(xiàn)自己的starter依賴

    本文主要介紹了Maven實現(xiàn)自己的starter依賴,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • 淺談常用字符串與集合類轉(zhuǎn)換的工具類

    淺談常用字符串與集合類轉(zhuǎn)換的工具類

    下面小編就為大家?guī)硪黄獪\談常用字符串與集合類轉(zhuǎn)換的工具類。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-08-08
  • RocketMQ的push消費(fèi)方式實現(xiàn)示例

    RocketMQ的push消費(fèi)方式實現(xiàn)示例

    這篇文章主要為大家介紹了RocketMQ的push消費(fèi)方式實現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>
    2022-08-08
  • Spring Cloud Alibaba Nacos Config進(jìn)階使用

    Spring Cloud Alibaba Nacos Config進(jìn)階使用

    這篇文章主要介紹了Spring Cloud Alibaba Nacos Config進(jìn)階使用,文中使用企業(yè)案例,圖文并茂的展示了Nacos Config的使用,感興趣的小伙伴可以看一看
    2021-08-08
  • Maven Repository倉庫的具體使用

    Maven Repository倉庫的具體使用

    本文主要介紹了Maven Repository倉庫的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-05-05
  • 新的Java訪問mysql數(shù)據(jù)庫工具類的操作代碼

    新的Java訪問mysql數(shù)據(jù)庫工具類的操作代碼

    本文通過實例代碼給大家介紹新的Java訪問mysql數(shù)據(jù)庫工具類的方法,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2021-12-12

最新評論