你都理解創(chuàng)建線程池的參數(shù)嗎?
多線程可以說是面試官最喜歡拿來問的題目之一了,可謂是老生之常談,不管你是新手還是老司機(jī),我相信你一定會在面試過程中遇到過有關(guān)多線程的一些問題。那我現(xiàn)在就充當(dāng)一次面試官,我來問你:
現(xiàn)有一個線程池,參數(shù)corePoolSize = 5,maximumPoolSize = 10,BlockingQueue阻塞隊(duì)列長度為5,此時有4個任務(wù)同時進(jìn)來,問:線程池會創(chuàng)建幾條線程?
如果4個任務(wù)還沒處理完,這時又同時進(jìn)來2個任務(wù),問:線程池又會創(chuàng)建幾條線程還是不會創(chuàng)建?
如果前面6個任務(wù)還是沒有處理完,這時又同時進(jìn)來5個任務(wù),問:線程池又會創(chuàng)建幾條線程還是不會創(chuàng)建?
如果你此時一臉懵逼,請不要慌,問題不大。
創(chuàng)建線程池的構(gòu)造方法的參數(shù)都有哪些?
要回答這個問題,我們需要從創(chuàng)建線程池的參數(shù)去找答案:
java.util.concurrent.ThreadPoolExecutor#ThreadPoolExecutor:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0) throw new IllegalArgumentException(); if (workQueue == null || threadFactory == null || handler == null) throw new NullPointerException(); this.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; }
創(chuàng)建線程池一共有7個參數(shù),從源碼可知,corePoolSize和maximumPoolSize都不能小于0,且核心線程數(shù)不能大于最大線程數(shù)。
下面我來解釋一下這7個參數(shù)的用途:
corePoolSize
線程池核心線程數(shù)量,核心線程不會被回收,即使沒有任務(wù)執(zhí)行,也會保持空閑狀態(tài)。
maximumPoolSize
池允許最大的線程數(shù),當(dāng)線程數(shù)量達(dá)到corePoolSize,且workQueue隊(duì)列塞滿任務(wù)了之后,繼續(xù)創(chuàng)建線程。
keepAliveTime
超過corePoolSize之后的“臨時線程”的存活時間。
unit
keepAliveTime的單位。
workQueue
當(dāng)前線程數(shù)超過corePoolSize時,新的任務(wù)會處在等待狀態(tài),并存在workQueue中,BlockingQueue是一個先進(jìn)先出的阻塞式隊(duì)列實(shí)現(xiàn),底層實(shí)現(xiàn)會涉及Java并發(fā)的AQS機(jī)制,有關(guān)于AQS的相關(guān)知識,我會單獨(dú)寫一篇,敬請期待。
threadFactory
創(chuàng)建線程的工廠類,通常我們會自頂一個threadFactory設(shè)置線程的名稱,這樣我們就可以知道線程是由哪個工廠類創(chuàng)建的,可以快速定位。
handler
線程池執(zhí)行拒絕策略,當(dāng)線數(shù)量達(dá)到maximumPoolSize大小,并且workQueue也已經(jīng)塞滿了任務(wù)的情況下,線程池會調(diào)用handler拒絕策略來處理請求。
系統(tǒng)默認(rèn)的拒絕策略有以下幾種:
- AbortPolicy:為線程池默認(rèn)的拒絕策略,該策略直接拋異常處理。
- DiscardPolicy:直接拋棄不處理。
- DiscardOldestPolicy:丟棄隊(duì)列中最老的任務(wù)。
- CallerRunsPolicy:將任務(wù)分配給當(dāng)前執(zhí)行execute方法線程來處理。
我們還可以自定義拒絕策略,只需要實(shí)現(xiàn)RejectedExecutionHandler接口即可,友好的拒絕策略實(shí)現(xiàn)有如下:
- 將數(shù)據(jù)保存到數(shù)據(jù),待系統(tǒng)空閑時再進(jìn)行處理
- 將數(shù)據(jù)用日志進(jìn)行記錄,后由人工處理
現(xiàn)在我們回到剛開始的問題就很好回答了:
線程池corePoolSize=5,線程初始化時不會自動創(chuàng)建線程,所以當(dāng)有4個任務(wù)同時進(jìn)來時,執(zhí)行execute方法會新建【4】條線程來執(zhí)行任務(wù);
前面的4個任務(wù)都沒完成,現(xiàn)在又進(jìn)來2個隊(duì)列,會新建【1】條線程來執(zhí)行任務(wù),這時poolSize=corePoolSize,還剩下1個任務(wù),線程池會將剩下這個任務(wù)塞進(jìn)阻塞隊(duì)列中,等待空閑線程執(zhí)行;
如果前面6個任務(wù)還是沒有處理完,這時又同時進(jìn)來了5個任務(wù),此時還沒有空閑線程來執(zhí)行新來的任務(wù),所以線程池繼續(xù)將這5個任務(wù)塞進(jìn)阻塞隊(duì)列,但發(fā)現(xiàn)阻塞隊(duì)列已經(jīng)滿了,核心線程也用完了,還剩下1個任務(wù)不知道如何是好,于是線程池只能創(chuàng)建【1】條“臨時”線程來執(zhí)行這個任務(wù)了;
這里創(chuàng)建的線程用“臨時”來描述還是因?yàn)樗鼈儾粫L期存在于線程池,它們的存活時間為keepAliveTime,此后線程池會維持最少corePoolSize數(shù)量的線程。
為什么不建議使用Executors創(chuàng)建線程池?
JDK為我們提供了Executors線程池工具類,里面有默認(rèn)的線程池創(chuàng)建策略,大概有以下幾種:
- FixedThreadPool:線程池線程數(shù)量固定,即corePoolSize和maximumPoolSize數(shù)量一樣。
- SingleThreadPool:單個線程的線程池。
- CachedThreadPool:初始核心線程數(shù)量為0,最大線程數(shù)量為Integer.MAX_VALUE,線程空閑時存活時間為60秒,并且它的阻塞隊(duì)列為SynchronousQueue,它的初始長度為0,這會導(dǎo)致任務(wù)每次進(jìn)來都會創(chuàng)建線程來執(zhí)行,在線程空閑時,存活時間到了又會釋放線程資源。
- ScheduledThreadPool:創(chuàng)建一個定長的線程池,而且支持定時的以及周期性的任務(wù)執(zhí)行,類似于Timer。
用Executors工具類雖然很方便,我依然不推薦大家使用以上默認(rèn)的線程池創(chuàng)建策略,阿里巴巴開發(fā)手冊也是強(qiáng)制不允許使用Executors來創(chuàng)建線程池,我們從JDK源碼中尋找一波答案:
java.util.concurrent.Executors:
// FixedThreadPool public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } // SingleThreadPool public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); } // CachedThreadPool public static ExecutorService newCachedThreadPool() { // 允許創(chuàng)建線程數(shù)為Integer.MAX_VALUE return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); } // ScheduledThreadPool public ScheduledThreadPoolExecutor(int corePoolSize) { // 允許創(chuàng)建線程數(shù)為Integer.MAX_VALUE super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
public LinkedBlockingQueue() { // 允許隊(duì)列長度最大為Integer.MAX_VALUE this(Integer.MAX_VALUE); }
從JDK源碼可看出,Executors工具類無非是把一些特定參數(shù)進(jìn)行了封裝,并提供一些方法供我們調(diào)用而已,我們并不能靈活地填寫參數(shù),策略過于簡單,不夠友好。
CachedThreadPool和ScheduledThreadPool最大線程數(shù)為Integer.MAX_VALUE,如果線程無限地創(chuàng)建,會造成OOM異常。
LinkedBlockingQueue基于鏈表的FIFO隊(duì)列,是無界的,默認(rèn)大小是Integer.MAX_VALUE,因此FixedThreadPool和SingleThreadPool的阻塞隊(duì)列長度為Integer.MAX_VALUE,如果此時隊(duì)列被無限地堆積任務(wù),會造成OOM異常。
以上所述是小編給大家介紹的創(chuàng)建線程池參數(shù)詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- java線程池使用后到底要關(guān)閉嗎
- Java實(shí)現(xiàn)終止線程池中正在運(yùn)行的定時任務(wù)
- Java手寫線程池的實(shí)現(xiàn)方法
- java簡單實(shí)現(xiàn)多線程及線程池實(shí)例詳解
- Java并發(fā)之串行線程池實(shí)例解析
- Java ExecutorService四種線程池使用詳解
- Java線程池FutureTask實(shí)現(xiàn)原理詳解
- java線程池:獲取運(yùn)行線程數(shù)并控制線程啟動速度的方法
- 詳談Java幾種線程池類型介紹及使用方法
- Java 線程池ExecutorService詳解及實(shí)例代碼
相關(guān)文章
SpringBoot項(xiàng)目啟動時如何讀取配置以及初始化資源
這篇文章主要給大家介紹了關(guān)于SpringBoot項(xiàng)目啟動時如何讀取配置以及初始化資源的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者使用SpringBoot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06java 操作gis geometry類型數(shù)據(jù)方式
這篇文章主要介紹了java 操作gis geometry類型數(shù)據(jù)方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03