聊一聊一道關(guān)于線程池的面試題
網(wǎng)絡(luò)上有這樣一道關(guān)于線程池的面試題:
1. 高并發(fā)、任務(wù)執(zhí)行時間短的業(yè)務(wù)怎樣使用線程池?
2. 并發(fā)不高、任務(wù)執(zhí)行時間長的業(yè)務(wù)怎樣使用線程池?
3. 并發(fā)高、業(yè)務(wù)執(zhí)行時間長的業(yè)務(wù)怎樣使用線程池?
請讀者思考下,如果你在面試中遇到這樣的問題該如何作答。
當(dāng)然,如果你僅把它當(dāng)做面試題,那就太遺憾了, 這是一個非常好的問題,能反映出開發(fā)者對線程池的理解深入程度以及對高性能服務(wù)結(jié)構(gòu)的設(shè)計能力。
線程池本質(zhì)上是生產(chǎn)者和消費(fèi)者模型,包括三要素:
往線程池隊列中投遞任務(wù)的生產(chǎn)者;
任務(wù)池隊列;
從任務(wù)池隊列取出任務(wù)執(zhí)行的 worker 線程(消費(fèi)者)。
要想合理的配置線程池的大小,得分析線程池任務(wù)的特性,可以從以下幾個方面來分析:
根據(jù)任務(wù)的性質(zhì)來分:
CPU 密集型任務(wù);
IO 密集型任務(wù);
混合型任務(wù)。
根據(jù)任務(wù)的優(yōu)先級:
高
中
低
根據(jù)任務(wù)的執(zhí)行時間:
長
中
短
不同性質(zhì)的任務(wù)可以交給不同配置的線程池執(zhí)行。
對于不同性質(zhì)的任務(wù)來說,CPU 密集型任務(wù)應(yīng)配置盡可能小的線程,如配置 CPU 個數(shù) + 1的線程數(shù);IO 密集型任務(wù)應(yīng)配置盡可能多的線程,因為 IO 操作不占用 CPU,不要讓 CPU 閑下來,應(yīng)加大線程數(shù)量,如配置兩倍 CPU 個數(shù) + 1;而對于混合型的任務(wù),如果可以拆分,拆分成 IO 密集型和 CPU 密集型分別處理,前提是兩者運(yùn)行的時間是差不多的,如果處理時間相差很大,則沒必要拆分了。
如果任務(wù)執(zhí)行時間長,在 worker 線程數(shù)量有限的情況下,worker 很快就很被任務(wù)占完,導(dǎo)致后續(xù)任務(wù)不能及時被處理,此時應(yīng)適當(dāng)增加 worker 線程數(shù)量;反過來,如果任務(wù)執(zhí)行時間短,那么 worker 線程數(shù)量不用太多,太多的 worker 線程會導(dǎo)致過多的時間浪費(fèi)在線程上下文切換上。
回到這個問題本身來,這里的“高并發(fā)”應(yīng)該是生產(chǎn)者生產(chǎn)任務(wù)的速度比較快,此時需要適當(dāng)增大任務(wù)隊列上限。
但是對于第三個問題并發(fā)高、業(yè)務(wù)執(zhí)行時間長這種情形單純靠線程池解決方案是不合適的,即使服務(wù)器有再高的資源配置,每個任務(wù)長周期地占用著資源,最終服務(wù)器資源也會很快被耗盡,因此對于這種情況,應(yīng)該配合業(yè)務(wù)解耦,做些模塊拆分優(yōu)化整個系統(tǒng)結(jié)構(gòu)。
關(guān)于線程池的設(shè)計,推薦給想提高的同學(xué)可以好好研究一下 JDK 的?ThreadExecutorPool?類及其幾種線程池模式。
