java 中四種線程池及 poolSize、corePoolSize、maximumPoolSize
Executors 提供四種線程池:
- newCachedThreadPool :緩存線程池,如果線程池長度超過處理需要,可回收空閑線程,若無可回收,則新建線程。
- newFixedThreadPool : 定長線程池,可控制線程最大並發數,超出的線程會在隊列中等待。
- newScheduledThreadPool : 計划線程池,支持定時及周期性任務執行。
- newSingleThreadExecutor :單線程線程池,用唯一的線程來執行任務,保證所有任務按照指定順序 (FIFO, LIFO, 優先級) 執行。
ThreadPoolExecutor 有幾個重要的成員變量:keepAliveTime、allowCoreThreadTimeOut、poolSize、corePoolSize、maximumPoolSize。下面分別介紹一下:
*corePoolSize*:線程池的基本大小。下面會解釋什么是基本大小。
maximumPoolSize:線程池中允許的最大線程數。
注意還有一個 largestPoolSize,記錄了曾經出現的最大線程個數。因為 setMaximumPoolSize() 可以改變最大線程數。
poolSize:線程池中當前線程的數量。
那么 poolSize、corePoolSize、maximumPoolSize 三者的關系是如何的呢?
當新提交一個任務時:
(1)如果 poolSize<corePoolSize,新增加一個線程處理新的任務。
(2)如果 poolSize=corePoolSize,新任務會被放入阻塞隊列等待。
(3)如果阻塞隊列的容量達到上限,且這時 poolSize<maximumPoolSize,新增線程來處理任務。
(4)如果阻塞隊列滿了,且 poolSize=maximumPoolSize,那么線程池已經達到極限,會根據飽和策略 RejectedExecutionHandler 拒絕新的任務。
所以通過上面的描述可知 corePoolSize<=maximumPoolSize,poolSize<=maximumPoolSize;而 poolSize 和 corePoolSize 無法比較,poolSize 是有可能比 corePoolSize 大的。
那么 corePoolSize、maximumPoolSize 在上面四種線程池中如何設定的?
通過幾個 newXXX 函數的源碼就可以知道,源碼如下:
(1)
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
定長線程池的 corePoolSize、maximumPoolSize 相同。都是設定值。
(2)
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
緩存線程池 corePoolSize 為 0,maximumPoolSize 則是 int 最大值。
(3)
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
單線程線程池 corePoolSize 和 maximumPoolSize 都是 1。
(4)
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
計划線程池用的是 ThreadPoolExecutor 的一個子類,可以看到 corePoolSize 是定義的,而 maximumPoolSize 則是 int 最大值。
注意這里的 corePoolSize、maximumPoolSize 不是最終的,因為可以通過 setCorePoolSize 和 setMaximumPoolSize() 改變。
上面提到阻塞隊列的飽和,那么這個飽和值是多少呢?
通過上面的代碼可以看到
(1)定長線程池和單線程線程都使用 LinkedBlockingQueue,而 LinkedBlockingQueue 默認的大小是 int 的最大值,如下:
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
(2)計划線程池使用的是 DelayedWordQueue,它默認大小是 16,但是可以動態增長,最大值則是 int 的最大值,如下:
private static final int INITIAL_CAPACITY = 16;
private RunnableScheduledFuture<?>[] queue =
new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private void grow() {
int oldCapacity = queue.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50%
if (newCapacity < 0) // overflow
newCapacity = Integer.MAX_VALUE;
queue = Arrays.copyOf(queue, newCapacity);
}
(3)緩存線程池使用的則是 SynchronousQueue,這個比較特殊沒有所謂的飽和值,而且前面也看到了緩存線程池的 corePoolSize 默認是 0。所以它新建一個線程與 SynchronousQueue 的機制有關,這里不展開說了,有興趣的可以研究一下這個類。
allowCoreThreadTimeOut:
是否允許核心線程超時退出。
如果該值為 false,且 poolSize<=corePoolSize,線程池都會保證這些核心線程處於存活狀態,不會超時退出。
如果為 true,則不論 poolSize 的大小,都允許超時退出。
如果 poolSize>corePoolSize,則該參數不論 true 還是 false,都允許超時退出。
相關判斷如下:
(poolSize> corePoolSize || allowCoreThreadTimeOut)
keepAliveTime:
如果一個線程處在空閑狀態的時間超過了該屬性值,就會因為超時而退出。是否允許超時退出則取決於上面的邏輯。