摘要:超過之后的臨時線程的存活時間。為什么不建議使用創建線程池為我們提供了線程池工具類,里面有默認的線程池創建策略,大概有以下幾種線程池線程數量固定,即和數量一樣。創建一個定長的線程池,而且支持定時的以及周期性的任務執行,類似于。
微信公眾號「后端進階」,專注后端技術分享:Java、Golang、WEB框架、分布式中間件、服務治理等等。
老司機傾囊相授,帶你一路進階,來不及解釋了快上車!
多線程可以說是面試官最喜歡拿來問的題目之一了,可謂是老生之常談,不管你是新手還是老司機,我相信你一定會在面試過程中遇到過有關多線程的一些問題。那我現在就充當一次面試官,我來問你:
現有一個線程池,參數corePoolSize = 5,maximumPoolSize = 10,BlockingQueue阻塞隊列長度為5,此時有4個任務同時進來,問:線程池會創建幾條線程?
如果4個任務還沒處理完,這時又同時進來2個任務,問:線程池又會創建幾條線程還是不會創建?
如果前面6個任務還是沒有處理完,這時又同時進來5個任務,問:線程池又會創建幾條線程還是不會創建?
如果你此時一臉懵逼,請不要慌,問題不大。
創建線程池的構造方法的參數都有哪些?要回答這個問題,我們需要從創建線程池的參數去找答案:
java.util.concurrent.ThreadPoolExecutor#ThreadPoolExecutor:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueworkQueue, 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; }
創建線程池一共有7個參數,從源碼可知,corePoolSize和maximumPoolSize都不能小于0,且核心線程數不能大于最大線程數。
下面我來解釋一下這7個參數的用途:
corePoolSize線程池核心線程數量,核心線程不會被回收,即使沒有任務執行,也會保持空閑狀態。如果線程池中的線程少于此數目,則在執行任務時創建。
maximumPoolSize池允許最大的線程數,當線程數量達到corePoolSize,且workQueue隊列塞滿任務了之后,繼續創建線程。
keepAliveTime超過corePoolSize之后的“臨時線程”的存活時間。
unitkeepAliveTime的單位。
workQueue當前線程數超過corePoolSize時,新的任務會處在等待狀態,并存在workQueue中,BlockingQueue是一個先進先出的阻塞式隊列實現,底層實現會涉及Java并發的AQS機制,有關于AQS的相關知識,我會多帶帶寫一篇,敬請期待。
threadFactory創建線程的工廠類,通常我們會自頂一個threadFactory設置線程的名稱,這樣我們就可以知道線程是由哪個工廠類創建的,可以快速定位。
handler線程池執行拒絕策略,當線數量達到maximumPoolSize大小,并且workQueue也已經塞滿了任務的情況下,線程池會調用handler拒絕策略來處理請求。
系統默認的拒絕策略有以下幾種:
AbortPolicy:為線程池默認的拒絕策略,該策略直接拋異常處理。
DiscardPolicy:直接拋棄不處理。
DiscardOldestPolicy:丟棄隊列中最老的任務。
CallerRunsPolicy:將任務分配給當前執行execute方法線程來處理。
我們還可以自定義拒絕策略,只需要實現RejectedExecutionHandler接口即可,友好的拒絕策略實現有如下:
將數據保存到數據,待系統空閑時再進行處理
將數據用日志進行記錄,后由人工處理
現在我們回到剛開始的問題就很好回答了:
線程池corePoolSize=5,線程初始化時不會自動創建線程,所以當有4個任務同時進來時,執行execute方法會新建【4】條線程來執行任務;
前面的4個任務都沒完成,現在又進來2個隊列,會新建【1】條線程來執行任務,這時poolSize=corePoolSize,還剩下1個任務,線程池會將剩下這個任務塞進阻塞隊列中,等待空閑線程執行;
如果前面6個任務還是沒有處理完,這時又同時進來了5個任務,此時還沒有空閑線程來執行新來的任務,所以線程池繼續將這5個任務塞進阻塞隊列,但發現阻塞隊列已經滿了,核心線程也用完了,還剩下1個任務不知道如何是好,于是線程池只能創建【1】條“臨時”線程來執行這個任務了;
這里創建的線程用“臨時”來描述還是因為它們不會長期存在于線程池,它們的存活時間為keepAliveTime,此后線程池會維持最少corePoolSize數量的線程。
為什么不建議使用Executors創建線程池?JDK為我們提供了Executors線程池工具類,里面有默認的線程池創建策略,大概有以下幾種:
FixedThreadPool:線程池線程數量固定,即corePoolSize和maximumPoolSize數量一樣。
SingleThreadPool:單個線程的線程池。
CachedThreadPool:初始核心線程數量為0,最大線程數量為Integer.MAX_VALUE,線程空閑時存活時間為60秒,并且它的阻塞隊列為SynchronousQueue,它的初始長度為0,這會導致任務每次進來都會創建線程來執行,在線程空閑時,存活時間到了又會釋放線程資源。
ScheduledThreadPool:創建一個定長的線程池,而且支持定時的以及周期性的任務執行,類似于Timer。
用Executors工具類雖然很方便,我依然不推薦大家使用以上默認的線程池創建策略,阿里巴巴開發手冊也是強制不允許使用Executors來創建線程池,我們從JDK源碼中尋找一波答案:
java.util.concurrent.Executors:
// FixedThreadPool public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); } // SingleThreadPool public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue ())); } // CachedThreadPool public static ExecutorService newCachedThreadPool() { // 允許創建線程數為Integer.MAX_VALUE return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue ()); } // ScheduledThreadPool public ScheduledThreadPoolExecutor(int corePoolSize) { // 允許創建線程數為Integer.MAX_VALUE super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedWorkQueue()); }
public LinkedBlockingQueue() { // 允許隊列長度最大為Integer.MAX_VALUE this(Integer.MAX_VALUE); }
從JDK源碼可看出,Executors工具類無非是把一些特定參數進行了封裝,并提供一些方法供我們調用而已,我們并不能靈活地填寫參數,策略過于簡單,不夠友好。
CachedThreadPool和ScheduledThreadPool最大線程數為Integer.MAX_VALUE,如果線程無限地創建,會造成OOM異常。
LinkedBlockingQueue基于鏈表的FIFO隊列,是無界的,默認大小是Integer.MAX_VALUE,因此FixedThreadPool和SingleThreadPool的阻塞隊列長度為Integer.MAX_VALUE,如果此時隊列被無限地堆積任務,會造成OOM異常。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/74804.html
摘要:有細心的網友早就想到了這個問題在線程池中,還有一些不常用的設置。所以該方法會在線程池總預先創建沒有任務執行的線程,數量為。下面我們測試一下從測試結果來看,線程池中已經預先創建了數量的空閑線程。 微信公眾號「后端進階」,專注后端技術分享:Java、Golang、WEB框架、分布式中間件、服務治理等等。 老司機傾囊相授,帶你一路進階,來不及解釋了快上車! 看完我上一篇文章「你都理解創建線...
摘要:你僅僅需要一個大小為數據庫連接池,然后讓剩下的業務線程都在隊列里等待就可以了。你應該經常會看到一些用戶量不是很大的應用中,為應付大約十來個的并發,卻將數據庫連接池設置成,的情況。請不要過度配置您的數據庫連接池的大小。 文章翻譯整理自: https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing歡迎關注個人微信公眾...
摘要:你僅僅需要一個大小為數據庫連接池,然后讓剩下的業務線程都在隊列里等待就可以了。你應該經常會看到一些用戶量不是很大的應用中,為應付大約十來個的并發,卻將數據庫連接池設置成,的情況。請不要過度配置您的數據庫連接池的大小。 文章翻譯整理自: https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing歡迎關注個人微信公眾...
摘要:參數說明,線程池保留的最小線程數。,線程池中允許擁有的最大線程數。,線程池的運行狀態。除非線程池狀態發生了變化,發退回到外層循環重新執行,判斷線程池的狀態。是線程池的核心控制狀態,包含的線程池運行狀態和有效線程數。 Java是一門多線程的語言,基本上生產環境的Java項目都離不開多線程。而線程則是其中最重要的系統資源之一,如果這個資源利用得不好,很容易導致程序低效率,甚至是出問題。 有...
閱讀 2604·2021-11-02 14:39
閱讀 4322·2021-10-11 10:58
閱讀 1446·2021-09-06 15:12
閱讀 1837·2021-09-01 10:49
閱讀 1326·2019-08-29 18:31
閱讀 1882·2019-08-29 16:10
閱讀 3331·2019-08-28 18:21
閱讀 867·2019-08-26 10:42