ThreadPoolExecutor 入參 corePoolSize 和 maximumPoolSize 的聯系


前言
我們可以通過 java.util.concurrent.ThreadPoolExecutor 來創建一個線程池:
new  ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds, runnableTaskQueue, threadFactory, handler);
參數說明:
1、corePoolSize(線程池的基本大小):當提交一個任務到線程池時,線程池會創建一個線程來執行任務,即使其他空閑的基本線程能夠執行新任務也會創建線程,等到需要執行的任務數大於線程池基本大小時就不再創建。如果調用了線程池的prestartAllCoreThreads方法,線程池會提前創建並啟動所有基本線程。
2、maximumPoolSize(線程池最大數量):線程池允許創建的最大線程數。如果隊列滿了,並且已創建的線程數小於最大線程數,則線程池會再創建新的線程執行任務。值得注意的是,如果使用了無界的任務隊列這個參數就沒用了。
3、keepAliveTime(線程活動時間):線程池的工作線程空閑后,保持存活的時間。所以如果任務很多,並且每個任務執行的時間比較短,可以調大時間,提高線程利用率。
4、TimeUnit(線程活動時間的單位):可選的單位有天(Days)、小時(HOURS)、分鍾(MINUTES)、毫秒(MILLISECONDS)、微秒(MICROSECONDS,千分之一毫秒)和納秒(NANOSECONDS,千分之一微秒)
5、runnableTaskQueue(任務隊列):用於保存等待執行的任務的阻塞隊列。 可以選擇以下幾個阻塞隊列:
(1)ArrayBlockingQueue:是一個基於數組結構的有界阻塞隊列,FIFO(先進先出)。
(2)LinkedBlockingQueue:一個基於鏈表結構的阻塞隊列,此隊列按FIFO (先進先出) 排序元素,吞吐量通常要高於ArrayBlockingQueue,靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列。
(3)SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處於阻塞狀態,吞吐量通常要高於LinkedBlockingQueue,靜態工廠方法Executors.newCachedThreadPool()使用了這個隊列。
(4)PriorityBlockingQueue:一個具有優先級的無限阻塞隊列。
6、threadFactory:用於設置創建線程的工廠,可以通過線程工廠給每個創建出來的線程設置更有意義的名字。
7、RejectedExecutionHandler (飽和策略):當隊列和線程池都滿了,說明線程池處於飽和狀態,那么必須采取一種策略還處理新提交的任務。它可以有如下四個選項:
AbortPolicy:直接拋出異常,默認情況下采用這種策略
CallerRunsPolicy:只用調用者所在線程來運行任務
DiscardOldestPolicy:丟棄隊列里最近的一個任務,並執行當前任務
DiscardPolicy:不處理,丟棄掉

按照一般的理解,初始化線程池,只需要一個 maximumPoolSize 入參就行了,corePoolSize 和 maximumPoolSize 似乎有重復的嫌疑(一開始我也是這么以為的),其實不是這樣的,下面我們來詳細說說這兩者的區別和聯系。

要理解 這兩個參數的區別,首先要知道,關於 ThreadPoolExecutor 相關的任務線程,它包含兩部分:(1)正在線程池中運行的任務線程、(2)在taskQueue 中排隊等待運行的任務線程。

1、當線程池初始化完成之后,executorService.submit(new Thread(...)); 加入需要運行的任務線程,因為線程池初始化是沒有線程運行的,所以當提交一個任務到線程池時,線程池會創建一個線程來直接執行任務;
2、當線程池中正在運行的線程達到 corePoolSize 個時,線程會放到 taskQueue 中排隊等候;
3、當 taskQueue(阻塞隊列)的容量達到上限(即隊列中不能再加入任務線程了),而當前的poolSize(就是正在線程池中運行的任務線程個數)小於 maximumPoolSize 時,則新增線程來處理任務;
4、當 taskQueue 的容量達到上限,且 poolSize = maximumPoolSize,那么線程池已經達到極限,會根據飽和策略RejectedExecutionHandler拒絕新的任務。

這個過程,像極了小朋友做客的場景:

小朋友做客,桌上放了一桌的土豆條,小朋友有一個碗(任務隊列 taskQueue),碗里可以放10根土豆條(taskQueue容量 = 10),正常情況下,小朋友每次可以吃2根土豆條(嘴巴就是線程池,corePoolSize = 2),小朋友狼吞虎咽的吃每次可以吃4根土豆條(maximumPoolSize = 4)。
坐在餐桌邊后,小朋友開始吃土豆,先往嘴里放了2根土豆條,但怕其他小朋友和自己搶,然后趕緊往自己的碗里加土豆條(加入隊列),直到碗滿了(現在小朋友嘴里2個土豆條,碗里10個土豆條)。小朋友依然害怕因為自己動作慢比其他人少吃,但是碗已經滿了,所以小朋友開始狼吞虎咽地吃(嘴里同時塞4個土豆條,碗里10個土豆條)。此時小朋友已經達到極限了,雖然桌上還有土豆條,但是也只能先拒絕了(飽和策略RejectedExecutionHandler),等嘴巴里的土豆條吃完之后,再去取。

補充:

如果 使用 LinkedBlockingQueue(使用無入參構造) 對象作為 taskQueue,則不容易出現隊列滿情況,而容易出現內存溢出情況。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM