日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長(zhǎng)提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747


【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)

 
 
 

在程序開發(fā)中,我們會(huì)用各種池化技術(shù)來(lái)緩存創(chuàng)建昂貴的對(duì)象,比如線程池、連接池、內(nèi)存池等。一般是預(yù)先創(chuàng)建一些對(duì)象放入池中,使用的時(shí)候直接取出使用,用完歸還以便復(fù)用。還會(huì)通過(guò)一定的策略調(diào)整池中緩存對(duì)象的數(shù)量,實(shí)現(xiàn)池的動(dòng)態(tài)伸縮。

由于線程的創(chuàng)建比較昂貴,隨意、沒有控制地創(chuàng)建大量線程會(huì)造成性能問題,因此,短平快的任務(wù)一般考慮使用線程池來(lái)處理,而不是直接創(chuàng)建線程。

今天,我們就針對(duì)線程池這個(gè)話題展開討論。通過(guò)三個(gè)生產(chǎn)事故,來(lái)看看使用線程池應(yīng)該注意些什么。

線程池的聲明需要手動(dòng)進(jìn)行

JAVA 中的 Executors 類定義了一些快捷的工具方法,來(lái)幫助我們快速創(chuàng)建線程池。不過(guò),《阿里巴巴 Java 開發(fā)手冊(cè)》中也提到,我們應(yīng)該禁止使用這些方法來(lái)創(chuàng)建線程池,而應(yīng)該手動(dòng)通過(guò) new ThreadPoolExecutor 來(lái)創(chuàng)建線程池。這一條規(guī)則的背后,是大量血淋淋的生產(chǎn)事故。最典型的就是 newFixedThreadPool 和 newCachedThreadPool 可能因?yàn)橘Y源耗盡而導(dǎo)致 OOM 問題。

首先,我們來(lái)看一下 newFixedThreadPool 為什么可能會(huì)出現(xiàn) OOM 的問題。

我們寫一段測(cè)試代碼,來(lái)初始化一個(gè)單線程的 FixedThreadPool,循環(huán) 1 億次向線程池提交任務(wù),每個(gè)任務(wù)都會(huì)創(chuàng)建一個(gè)比較大的字符串然后休眠一小時(shí):

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

執(zhí)行程序后不久,日志中就出現(xiàn)了如下 OOM:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

翻看 newFixedThreadPool 方法的源碼不難發(fā)現(xiàn),線程池的工作隊(duì)列直接 new 了一個(gè) LinkedBlockingQueue,而默認(rèn)構(gòu)造方法的 LinkedBlockingQueue 是一個(gè) Integer.MAX_VALUE 長(zhǎng)度的隊(duì)列,可以認(rèn)為是無(wú)界的:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

雖然使用 newFixedThreadPool 可以把工作線程控制在固定的數(shù)量上,但任務(wù)隊(duì)列是無(wú)界的。如果任務(wù)較多并且執(zhí)行較慢的話,隊(duì)列可能會(huì)快速積壓,撐爆內(nèi)存導(dǎo)致 OOM。

我們?cè)侔褎偛诺睦由晕⒏囊幌拢臑槭褂?newCachedThreadPool 方法來(lái)獲得線程池。程序運(yùn)行不久后,同樣看到了如下 OOM 異常:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

從日志中可以看到,這次 OOM 的原因是無(wú)法創(chuàng)建線程,翻看 newCachedThreadPool 的源碼可以看到,這種線程池的最大線程數(shù)是 Integer.MAX_VALUE,可以認(rèn)為是沒有上限的,而其工作隊(duì)列 SynchronousQueue 是一個(gè)沒有存儲(chǔ)空間的阻塞隊(duì)列。這意味著,只要有請(qǐng)求到來(lái),就必須找到一條工作線程來(lái)處理,如果當(dāng)前沒有空閑的線程就再創(chuàng)建一條新的。

由于我們的任務(wù)需要 1 小時(shí)才能執(zhí)行完成,大量的任務(wù)進(jìn)來(lái)后會(huì)創(chuàng)建大量的線程。我們知道線程是需要分配一定的內(nèi)存空間作為線程棧的,比如 1MB,因此無(wú)限制創(chuàng)建線程必然會(huì)導(dǎo)致 OOM:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

其實(shí),大部分 Java 開發(fā)同學(xué)知道這兩種線程池的特性,只是抱有僥幸心理,覺得只是使用線程池做一些輕量級(jí)的任務(wù),不可能造成隊(duì)列積壓或開啟大量線程。

但現(xiàn)實(shí)往往是殘酷的。我之前就遇到過(guò)這么一個(gè)事故:用戶注冊(cè)后,我們調(diào)用一個(gè)外部服務(wù)去發(fā)送短信,發(fā)送短信接口正常時(shí)可以在 100 毫秒內(nèi)響應(yīng),TPS 100 的注冊(cè)量,CachedThreadPool 能穩(wěn)定在占用 10 個(gè)左右線程的情況下滿足需求。在某個(gè)時(shí)間點(diǎn),外部短信服務(wù)不可用了,我們調(diào)用這個(gè)服務(wù)的超時(shí)又特別長(zhǎng), 比如 1 分鐘,1 分鐘可能就進(jìn)來(lái)了 6000 用戶,產(chǎn)生 6000 個(gè)發(fā)送短信的任務(wù),需要 6000 個(gè)線程,沒多久就因?yàn)闊o(wú)法創(chuàng)建線程導(dǎo)致了 OOM,整個(gè)應(yīng)用程序崩潰。

因此,我同樣不建議使用 Executors 提供的兩種快捷的線程池,原因如下:

  1. 我們需要根據(jù)自己的場(chǎng)景、并發(fā)情況來(lái)評(píng)估線程池的幾個(gè)核心參數(shù),包括核心線程數(shù)、最大線程數(shù)、線程回收策略、工作隊(duì)列的類型,以及拒絕策略,確保線程池的工作行為符合需求,一般都需要設(shè)置有界的工作隊(duì)列和可控的線程數(shù)。
  2. 任何時(shí)候,都應(yīng)該為自定義線程池指定有意義的名稱,以方便排查問題。當(dāng)出現(xiàn)線程數(shù)量暴增、線程死鎖、線程占用大量 CPU、線程執(zhí)行出現(xiàn)異常等問題時(shí),我們往往會(huì)抓取線程棧。此時(shí),有意義的線程名稱,就可以方便我們定位問題。

除了建議手動(dòng)聲明線程池以外,我還建議用一些監(jiān)控手段來(lái)觀察線程池的狀態(tài)。線程池這個(gè)組件往往會(huì)表現(xiàn)得任勞任怨、默默無(wú)聞,除非是出現(xiàn)了拒絕策略,否則壓力再大都不會(huì)拋出一個(gè)異常。如果我們能提前觀察到線程池隊(duì)列的積壓,或者線程數(shù)量的快速膨脹,往往可以提早發(fā)現(xiàn)并解決問題。

線程池線程管理策略詳解

在之前的 Demo 中,我們用一個(gè) printStats 方法實(shí)現(xiàn)了最簡(jiǎn)陋的監(jiān)控,每秒輸出一次線程池的基本內(nèi)部信息,包括線程數(shù)、活躍線程數(shù)、完成了多少任務(wù),以及隊(duì)列中還有多少積壓任務(wù)等信息:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

接下來(lái),我們就利用這個(gè)方法來(lái)觀察一下線程池的基本特性吧。

首先,自定義一個(gè)線程池。這個(gè)線程池具有 2 個(gè)核心線程、5 個(gè)最大線程、使用容量為 10 的 ArrayBlockingQueue 阻塞隊(duì)列作為工作隊(duì)列,使用默認(rèn)的 AbortPolicy 拒絕策略,也就是任務(wù)添加到線程池失敗會(huì)拋出 RejectedExecutionException。此外,我們借助了 Jodd 類庫(kù)的 ThreadFactoryBuilder 方法來(lái)構(gòu)造一個(gè)線程工廠,實(shí)現(xiàn)線程池線程的自定義命名。

然后,我們寫一段測(cè)試代碼來(lái)觀察線程池管理線程的策略。測(cè)試代碼的邏輯為每次間隔 1 秒向線程池提交任務(wù),循環(huán) 20 次,每個(gè)任務(wù)需要 10 秒才能執(zhí)行完成,代碼如下:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

60 秒后頁(yè)面輸出了 17,有 3 次提交失敗了:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

并且日志中也出現(xiàn)了 3 次類似的錯(cuò)誤信息:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

我們把 printStats 方法打印出的日志繪制成圖表,得出如下曲線:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

至此,我們可以總結(jié)出線程池默認(rèn)的工作行為:

  1. 不會(huì)初始化 corePoolSize 個(gè)線程,有任務(wù)來(lái)了才創(chuàng)建工作線程;
  2. 當(dāng)核心線程滿了之后不會(huì)立即擴(kuò)容線程池,而是把任務(wù)堆積到工作隊(duì)列中;
  3. 當(dāng)工作隊(duì)列滿了后擴(kuò)容線程池,一直到線程個(gè)數(shù)達(dá)到 maximumPoolSize 為止;
  4. 如果隊(duì)列已滿且達(dá)到了最大線程后還有任務(wù)進(jìn)來(lái),按照拒絕策略處理;
  5. 當(dāng)線程數(shù)大于核心線程數(shù)時(shí),線程等待 keepAliveTime 后還是沒有任務(wù)需要處理的話,收縮線程到核心線程數(shù)。

了解這個(gè)策略,有助于我們根據(jù)實(shí)際的容量規(guī)劃需求,為線程池設(shè)置合適的初始化參數(shù)。當(dāng)然,我們也可以通過(guò)一些手段來(lái)改變這些默認(rèn)工作行為,比如:

  1. 聲明線程池后立即調(diào)用 prestartAllCoreThreads 方法,來(lái)啟動(dòng)所有核心線程;
  2. 傳入 true 給 allowCoreThreadTimeOut 方法,來(lái)讓線程池在空閑的時(shí)候同樣回收核心線程。

不知道你有沒有想過(guò):Java 線程池是先用工作隊(duì)列來(lái)存放來(lái)不及處理的任務(wù),滿了之后再擴(kuò)容線程池。當(dāng)我們的工作隊(duì)列設(shè)置得很大時(shí),最大線程數(shù)這個(gè)參數(shù)顯得沒有意義,因?yàn)殛?duì)列很難滿,或者到滿的時(shí)候再去擴(kuò)容線程池已經(jīng)于事無(wú)補(bǔ)了。

那么,我們有沒有辦法讓線程池更激進(jìn)一點(diǎn),優(yōu)先開啟更多的線程,而把隊(duì)列當(dāng)成一個(gè)后備方案呢?比如我們這個(gè)例子,任務(wù)執(zhí)行得很慢,需要 10 秒,如果線程池可以優(yōu)先擴(kuò)容到 5 個(gè)最大線程,那么這些任務(wù)最終都可以完成,而不會(huì)因?yàn)榫€程池?cái)U(kuò)容過(guò)晚導(dǎo)致慢任務(wù)來(lái)不及處理。

限于篇幅,這里我只給你一個(gè)大致思路:

  1. 由于線程池在工作隊(duì)列滿了無(wú)法入隊(duì)的情況下會(huì)擴(kuò)容線程池,那么我們是否可以重寫隊(duì)列的 offer 方法,造成這個(gè)隊(duì)列已滿的假象呢?
  2. 由于我們 Hack 了隊(duì)列,在達(dá)到了最大線程后勢(shì)必會(huì)觸發(fā)拒絕策略,那么能否實(shí)現(xiàn)一個(gè)自定義的拒絕策略處理程序,這個(gè)時(shí)候再把任務(wù)真正插入隊(duì)列呢?

接下來(lái),就請(qǐng)你動(dòng)手試試看如何實(shí)現(xiàn)這樣一個(gè)“彈性”線程池吧。Tomcat 線程池也實(shí)現(xiàn)了類似的效果,可供你借鑒。

務(wù)必確認(rèn)清楚線程池本身是不是復(fù)用的

不久之前我遇到了這樣一個(gè)事故:某項(xiàng)目生產(chǎn)環(huán)境時(shí)不時(shí)有報(bào)警提示線程數(shù)過(guò)多,超過(guò) 2000 個(gè),收到報(bào)警后查看監(jiān)控發(fā)現(xiàn),瞬時(shí)線程數(shù)比較多但過(guò)一會(huì)兒又會(huì)降下來(lái),線程數(shù)抖動(dòng)很厲害,而應(yīng)用的訪問量變化不大。

為了定位問題,我們?cè)诰€程數(shù)比較高的時(shí)候進(jìn)行線程棧抓取,抓取后發(fā)現(xiàn)內(nèi)存中有 1000 多個(gè)自定義線程池。一般而言,線程池肯定是復(fù)用的,有 5 個(gè)以內(nèi)的線程池都可以認(rèn)為正常,而 1000 多個(gè)線程池肯定不正常。

在項(xiàng)目代碼里,我們沒有搜到聲明線程池的地方,搜索 execute 關(guān)鍵字后定位到,原來(lái)是業(yè)務(wù)代碼調(diào)用了一個(gè)類庫(kù)來(lái)獲得線程池,類似如下的業(yè)務(wù)代碼:調(diào)用 ThreadPoolHelper 的 getThreadPool 方法來(lái)獲得線程池,然后提交數(shù)個(gè)任務(wù)到線程池處理,看不出什么異常。

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

但是,來(lái)到 ThreadPoolHelper 的實(shí)現(xiàn)讓人大跌眼鏡,getThreadPool 方法居然是每次都使用 Executors.newCachedThreadPool 來(lái)創(chuàng)建一個(gè)線程池。

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

通過(guò)上一小節(jié)的學(xué)習(xí),我們可以想到 newCachedThreadPool 會(huì)在需要時(shí)創(chuàng)建必要多的線程,業(yè)務(wù)代碼的一次業(yè)務(wù)操作會(huì)向線程池提交多個(gè)慢任務(wù),這樣執(zhí)行一次業(yè)務(wù)操作就會(huì)開啟多個(gè)線程。如果業(yè)務(wù)操作并發(fā)量較大的話,的確有可能一下子開啟幾千個(gè)線程。

那為什么我們能在監(jiān)控中看到線程數(shù)量會(huì)下降,而不會(huì)撐爆內(nèi)存呢?

回到 newCachedThreadPool 的定義就會(huì)發(fā)現(xiàn),它的核心線程數(shù)是 0,而 keepAliveTime 是 60 秒,也就是在 60 秒之后所有的線程都是可以回收的。好吧,就因?yàn)檫@個(gè)特性,我們的業(yè)務(wù)程序死得沒太難看。

要修復(fù)這個(gè) Bug 也很簡(jiǎn)單,使用一個(gè)靜態(tài)字段來(lái)存放線程池的引用,返回線程池的代碼直接返回這個(gè)靜態(tài)字段即可。這里一定要記得我們的最佳實(shí)踐,手動(dòng)創(chuàng)建線程池。修復(fù)后的 ThreadPoolHelper 類如下:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

需要仔細(xì)斟酌線程池的混用策略

線程池的意義在于復(fù)用,那這是不是意味著程序應(yīng)該始終使用一個(gè)線程池呢?

當(dāng)然不是。這要根據(jù)任務(wù)的“輕重緩急”來(lái)指定線程池的核心參數(shù),包括線程數(shù)、回收策略和任務(wù)隊(duì)列:

  1. 對(duì)于執(zhí)行比較慢、數(shù)量不大的 IO 任務(wù),或許要考慮更多的線程數(shù),而不需要太大的隊(duì)列。
  2. 而對(duì)于吞吐量較大的計(jì)算型任務(wù),線程數(shù)量不宜過(guò)多,可以是 CPU 核數(shù)或核數(shù) *2(理由是,線程一定調(diào)度到某個(gè) CPU 進(jìn)行執(zhí)行,如果任務(wù)本身是 CPU 綁定的任務(wù),那么過(guò)多的線程只會(huì)增加線程切換的開銷,并不能提升吞吐量),但可能需要較長(zhǎng)的隊(duì)列來(lái)做緩沖。

之前我也遇到過(guò)這么一個(gè)問題,業(yè)務(wù)代碼使用了線程池異步處理一些內(nèi)存中的數(shù)據(jù),但通過(guò)監(jiān)控發(fā)現(xiàn)處理得非常慢,整個(gè)處理過(guò)程都是內(nèi)存中的計(jì)算不涉及 IO 操作,也需要數(shù)秒的處理時(shí)間,應(yīng)用程序 CPU 占用也不是特別高,有點(diǎn)不可思議。

經(jīng)排查發(fā)現(xiàn),業(yè)務(wù)代碼使用的線程池,還被一個(gè)后臺(tái)的文件批處理任務(wù)用到了。

或許是夠用就好的原則,這個(gè)線程池只有 2 個(gè)核心線程,最大線程也是 2,使用了容量為 100 的 ArrayBlockingQueue 作為工作隊(duì)列,使用了 CallerRunsPolicy 拒絕策略:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

這里,我們模擬一下文件批處理的代碼,在程序啟動(dòng)后通過(guò)一個(gè)線程開啟死循環(huán)邏輯,不斷向線程池提交任務(wù),任務(wù)的邏輯是向一個(gè)文件中寫入大量的數(shù)據(jù):

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

可以想象到,這個(gè)線程池中的 2 個(gè)線程任務(wù)是相當(dāng)重的。通過(guò) printStats 方法打印出的日志,我們觀察下線程池的負(fù)擔(dān):

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

可以看到,線程池的 2 個(gè)線程始終處于活躍狀態(tài),隊(duì)列也基本處于打滿狀態(tài)。因?yàn)殚_啟了 CallerRunsPolicy 拒絕處理策略,所以當(dāng)線程滿載隊(duì)列也滿的情況下,任務(wù)會(huì)在提交任務(wù)的線程,或者說(shuō)調(diào)用 execute 方法的線程執(zhí)行,也就是說(shuō)不能認(rèn)為提交到線程池的任務(wù)就一定是異步處理的。如果使用了 CallerRunsPolicy 策略,那么有可能異步任務(wù)變?yōu)橥綀?zhí)行。從日志的第四行也可以看到這點(diǎn)。這也是這個(gè)拒絕策略比較特別的原因。

不知道寫代碼的同學(xué)為什么設(shè)置這個(gè)策略,或許是測(cè)試時(shí)發(fā)現(xiàn)線程池因?yàn)槿蝿?wù)處理不過(guò)來(lái)出現(xiàn)了異常,而又不希望線程池丟棄任務(wù),所以最終選擇了這樣的拒絕策略。不管怎樣,這些日志足以說(shuō)明線程池是飽和狀態(tài)。

可以想象到,業(yè)務(wù)代碼復(fù)用這樣的線程池來(lái)做內(nèi)存計(jì)算,命運(yùn)一定是悲慘的。我們寫一段代碼測(cè)試下,向線程池提交一個(gè)簡(jiǎn)單的任務(wù),這個(gè)任務(wù)只是休眠 10 毫秒沒有其他邏輯:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

我們使用 wrk 工具對(duì)這個(gè)接口進(jìn)行一個(gè)簡(jiǎn)單的壓測(cè),可以看到 TPS 為 75,性能的確非常差。

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

細(xì)想一下,問題其實(shí)沒有這么簡(jiǎn)單。因?yàn)樵瓉?lái)執(zhí)行 IO 任務(wù)的線程池使用的是 CallerRunsPolicy 策略,所以直接使用這個(gè)線程池進(jìn)行異步計(jì)算的話,當(dāng)線程池飽和的時(shí)候,計(jì)算任務(wù)會(huì)在執(zhí)行 Web 請(qǐng)求的 Tomcat 線程執(zhí)行,這時(shí)就會(huì)進(jìn)一步影響到其他同步處理的線程,甚至造成整個(gè)應(yīng)用程序崩潰。

解決方案很簡(jiǎn)單,使用獨(dú)立的線程池來(lái)做這樣的“計(jì)算任務(wù)”即可。計(jì)算任務(wù)打了雙引號(hào),是因?yàn)槲覀兊哪M代碼執(zhí)行的是休眠操作,并不屬于 CPU 綁定的操作,更類似 IO 綁定的操作,如果線程池線程數(shù)設(shè)置太小會(huì)限制吞吐能力:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

使用單獨(dú)的線程池改造代碼后再來(lái)測(cè)試一下性能,TPS 提高到了 1727:

【系統(tǒng)架構(gòu)】使用線程池你需要注意這幾點(diǎn)
 
 
 

可以看到,盲目復(fù)用線程池混用線程的問題在于,別人定義的線程池屬性不一定適合你的任務(wù),而且混用會(huì)相互干擾。這就好比,我們往往會(huì)用虛擬化技術(shù)來(lái)實(shí)現(xiàn)資源的隔離,而不是讓所有應(yīng)用程序都直接使用物理機(jī)。

就線程池混用問題,我想再和你補(bǔ)充一個(gè)坑:Java 8 的 parallel stream 功能,可以讓我們很方便地并行處理集合中的元素,其背后是共享同一個(gè) ForkJoinPool,默認(rèn)并行度是 CPU 核數(shù) -1。對(duì)于 CPU 綁定的任務(wù)來(lái)說(shuō),使用這樣的配置比較合適,但如果集合操作涉及同步 IO 操作的話(比如數(shù)據(jù)庫(kù)操作、外部服務(wù)調(diào)用等),建議自定義一個(gè) ForkJoinPool(或普通線程池)。

重點(diǎn)回顧

線程池管理著線程,線程又屬于寶貴的資源,有許多應(yīng)用程序的性能問題都來(lái)自線程池的配置和使用不當(dāng)。在今天的學(xué)習(xí)中,我通過(guò)三個(gè)和線程池相關(guān)的生產(chǎn)事故,和你分享了使用線程池的幾個(gè)最佳實(shí)踐。

第一,Executors 類提供的一些快捷聲明線程池的方法雖然簡(jiǎn)單,但隱藏了線程池的參數(shù)細(xì)節(jié)。因此,使用線程池時(shí),我們一定要根據(jù)場(chǎng)景和需求配置合理的線程數(shù)、任務(wù)隊(duì)列、拒絕策略、線程回收策略,并對(duì)線程進(jìn)行明確的命名方便排查問題。

第二,既然使用了線程池就需要確保線程池是在復(fù)用的,每次 new 一個(gè)線程池出來(lái)可能比不用線程池還糟糕。如果你沒有直接聲明線程池而是使用其他同學(xué)提供的類庫(kù)來(lái)獲得一個(gè)線程池,請(qǐng)務(wù)必查看源碼,以確認(rèn)線程池的實(shí)例化方式和配置是符合預(yù)期的。

第三,復(fù)用線程池不代表應(yīng)用程序始終使用同一個(gè)線程池,我們應(yīng)該根據(jù)任務(wù)的性質(zhì)來(lái)選用不同的線程池。特別注意 IO 綁定的任務(wù)和 CPU 綁定的任務(wù)對(duì)于線程池屬性的偏好,如果希望減少任務(wù)間的相互干擾,考慮按需使用隔離的線程池。

最后,我想強(qiáng)調(diào)的是,線程池作為應(yīng)用程序內(nèi)部的核心組件往往缺乏監(jiān)控(如果你使用類似 RabbitMQ 這樣的 MQ 中間件,運(yùn)維同學(xué)一般會(huì)幫我們做好中間件監(jiān)控),往往到程序崩潰后才發(fā)現(xiàn)線程池的問題,很被動(dòng)。

分享到:
標(biāo)簽:線程
用戶無(wú)頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過(guò)答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫(kù),初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績(jī)?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績(jī)?cè)u(píng)定