ThreadPoolExecutor源碼解析(轉載)

轉載地址
轉載地址

線程池能夠對線程進行有效的管理, 複用和數量上限的限制, 如果你需要創建多個線程來執行多個異步任務, 那麼使用線程池顯然要比頻繁地 new Thread().start() 這種方式要好.

Java 中的線程池是用 ThreadPoolExecutor 類來表示的. 我們今天就結合該類的源碼來分析一下這個類內部對於線程的創建, 管理以及後臺任務的調度等方面的執行原理. 我這裏分析的是 Oracle JDK 1.8 的源碼.

1. ctl

ThreadPoolExecutor 類中有個非常重要的字段 ctl, ctl 其實可以理解爲單詞 control 的簡寫, 翻譯過來就是 “控制”, 具體來說就是對線程池的運行狀態和池子中有效線程的數量進行控制的一個字段. 我們看下該字段在源碼中的定義:

/**
 * The main pool control state, ctl, is an atomic integer packing
 * two conceptual fields
 *   workerCount, indicating the effective number of threads
 *   runState,    indicating whether running, shutting down etc
 *
 * In order to pack them into one int, we limit workerCount to
 * (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
 * billion) otherwise representable. If this is ever an issue in
 * the future, the variable can be changed to be an AtomicLong,
 * and the shift/mask constants below adjusted. But until the need
 * arises, this code is a bit faster and simpler using an int.
 *
 * The workerCount is the number of workers that have been
 * permitted to start and not permitted to stop.  The value may be
 * transiently different from the actual number of live threads,
 * for example when a ThreadFactory fails to create a thread when
 * asked, and when exiting threads are still performing
 * bookkeeping before terminating. The user-visible pool size is
 * reported as the current size of the workers set.
 *
 * The runState provides the main lifecycle control, taking on values:
 *
 *   RUNNING:  Accept new tasks and process queued tasks
 *   SHUTDOWN: Don't accept new tasks, but process queued tasks
 *   STOP:     Don't accept new tasks, don't process queued tasks,
 *             and interrupt in-progress tasks
 *   TIDYING:  All tasks have terminated, workerCount is zero,
 *             the thread transitioning to state TIDYING
 *             will run the terminated() hook method
 *   TERMINATED: terminated() has completed
 *
 * The numerical order among these values matters, to allow
 * ordered comparisons. The runState monotonically increases over
 * time, but need not hit each state. The transitions are:
 *
 * RUNNING -> SHUTDOWN
 *    On invocation of shutdown(), perhaps implicitly in finalize()
 * (RUNNING or SHUTDOWN) -> STOP
 *    On invocation of shutdownNow()
 * SHUTDOWN -> TIDYING
 *    When both queue and pool are empty
 * STOP -> TIDYING
 *    When pool is empty
 * TIDYING -> TERMINATED
 *    When the terminated() hook method has completed
 *
 * Threads waiting in awaitTermination() will return when the
 * state reaches TERMINATED.
 *
 * Detecting the transition from SHUTDOWN to TIDYING is less
 * straightforward than you'd like because the queue may become
 * empty after non-empty and vice versa during SHUTDOWN state, but
 * we can only terminate if, after seeing that it is empty, we see
 * that workerCount is 0 (which sometimes entails a recheck -- see
 * below).
 */
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

ctl 是一個 AtomicInteger 對象, 也就是一個特殊的 int 型變量, 特殊之處在於所有需要修改其數值的操作都是原子化的. 如果你不熟悉原子化 (atomic) 這個概念, 那麼你可以將它簡單理解爲 synchronized, 即: 所有修改其數值的操作都需要在加了同步鎖的情況下來進行.

一個 ctl 變量可以包含兩部分信息: 線程池的運行狀態 (runState) 和線程池內有效線程的數量 (workerCount). 由於 int 型的變量是由32位二進制的數構成, 所以用 ctl 的高3位來表示線程池的運行狀態, 用低29位來表示線程池內有效線程的數量. 由於這兩部分信息在該類中很多地方都會使用到, 所以我們也經常會涉及到要獲取其中一個信息的操作, 通常來說, 代表這兩個信息的變量的名稱直接用他們各自英文單詞首字母的組合來表示, 所以, 表示線程池運行狀態的變量通常命名爲 rs, 表示線程池中有效線程數量的變量通常命名爲 wc, 另外, ctl 也通常會簡寫作 c, 你一定要對這裏提到的幾個變量名稍微留個印象哦. 如果你在該類源碼的某個地方遇到了見名卻不知意的變量名時, 你在抱怨這糟糕的命名的時候, 要試着去核實一下, 那些變量是不是正是這裏提到的幾個信息哦.

由於 ctl 變量是由線程池的運行狀態 (runState) 和線程池內有效線程的數量 (workerCount)這兩個信息組合而成, 所以, 如果知道了這兩部分信息各自的數值, 就可以調用下面的 ctlOf() 方法來計算出 ctl 的數值:

// rs: 表示線程池的運行狀態 (rs 是 runState中各單詞首字母的簡寫組合)
// wc: 表示線程池內有效線程的數量 (wc 是 workerCount中各單詞首字母的簡寫組合)
private static int ctlOf(int rs, int wc) { return rs | wc; }
  • 1
  • 2
  • 3

反過來, 如果知道了 ctl 的值, 那麼也可以通過如下的 runStateOf() 和 workerCountOf() 兩個方法來分別獲取線程池的運行狀態和線程池內有效線程的數量.

private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }
  • 1
  • 2

其中, CAPACITY 等於 (2^29)-1, 也就是高3位是0, 低29位是1的一個int型的數,

private static final int COUNT_BITS = Integer.SIZE - 3;     // 29
private static final int CAPACITY = (1 << COUNT_BITS) - 1;  // COUNT_BITS == 29
  • 1
  • 2

所以上邊兩個方法的計算過程也就不難理解了吧 (ps: 如果此時你還是不理解這兩個方法的計算過程, 請先學習二進制位運算的相關知識, 然後再來看這兩個方法, 你會發現他們很容易理解的). 另外, CAPACITY 這個常量從名字上可以知道, 該常量表示某個容量值, 那麼表示的是什麼容量值呢? 其實, 我們前面介紹過, ctl 只用他的低29位來表示線程池內的有效線程數, 也就是說, 線程池內有效線程的數量上限就是29個二進制1所表示的數值 (約爲5億), 而線程池就是用 CAPACITY 這個常量來表示這個上限數值的.

下面再介紹下線程池的運行狀態. 線程池一共有五種狀態, 分別是:

① RUNNING (運行狀態): 能接受新提交的任務, 並且也能處理阻塞隊列中的任務. 
② SHUTDOWN (關閉狀態): 不再接受新提交的任務, 但卻可以繼續處理阻塞隊列中已保存的任務. 在線程池處於 RUNNING 狀態時, 調用 shutdown()方法會使線程池進入到該狀態. 當然, finalize() 方法在執行過程中或許也會隱式地進入該狀態. 
③ STOP : 不能接受新提交的任務, 也不能處理阻塞隊列中已保存的任務, 並且會中斷正在處理中的任務. 在線程池處於 RUNNING 或 SHUTDOWN 狀態時, 調用 shutdownNow() 方法會使線程池進入到該狀態. 
④ TIDYING (清理狀態): 所有的任務都已終止了, workerCount (有效線程數) 爲0, 線程池進入該狀態後會調用 terminated() 方法以讓該線程池進入TERMINATED 狀態. 當線程池處於 SHUTDOWN 狀態時, 如果此後線程池內沒有線程了並且阻塞隊列內也沒有待執行的任務了 (即: 二者都爲空), 線程池就會進入到該狀態. 當線程池處於 STOP 狀態時, 如果此後線程池內沒有線程了, 線程池就會進入到該狀態. 
⑤ TERMINATED : terminated() 方法執行完後就進入該狀態.

中文翻譯可能不太準確, 也不能充分表達源碼所表示的所有含義, 還可能造成歧義, 例如: STOP 和 TERMINATED 似乎翻譯過來的意思沒太大區別啊. 所以我們在描述線程池的運行狀態時, 建議直接使用上面的5個英文單詞來表示. 這五種狀態的具體數值如下:

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

前邊提到過 COUNT_BITS == 29. 其實我們只需要知道, 上邊這5個常量是按照從小到大的順序列出的即可. 如果你在源碼中看到 
rs < SHUTDOWN (假如用 rs 代表線程池的運行狀態), 那麼你要知道, 這表示線程池處於 RUNNING 狀態.

2. 幾個重要的參數

ThreadPoolExecutor 類的構造方法中提供了幾個非常重要的參數, 這幾個參數也對應着該類中的幾個同名的字段. 理解這幾個重要參數/字段的含義, 將有助於我們分析線程池對線程調度的原理. 下面我們就來看看該類的構造方法吧, 如下圖所示:

這裏寫圖片描述

前三個方法最終都會去調用第四個方法, 也就是參數數量最多的那個方法, 所以我們來看看這第四個方法的源碼, 如下:

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param corePoolSize the number of threads to keep in the pool, even
 *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
 * @param maximumPoolSize the maximum number of threads to allow in the
 *        pool
 * @param keepAliveTime when the number of threads is greater than
 *        the core, this is the maximum time that excess idle threads
 *        will wait for new tasks before terminating.
 * @param unit the time unit for the {@code keepAliveTime} argument
 * @param workQueue the queue to use for holding tasks before they are
 *        executed.  This queue will hold only the {@code Runnable}
 *        tasks submitted by the {@code execute} method.
 * @param threadFactory the factory to use when the executor
 *        creates a new thread
 * @param handler the handler to use when execution is blocked
 *        because the thread bounds and queue capacities are reached
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code threadFactory} or {@code handler} is null
 */
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.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

從上述源碼可知, 傳遞的參數必須符合如下要求:

(1) corePoolSize >= 0, maximumPoolSize > 0, maximumPoolSize >= corePoolSize, keepAliveTime >= 0 
(2) workQueue, threadFactory, handler 還不能爲null.

如果傳遞的所有參數都符合上述要求, 那麼就會執行後邊的6個賦值語句, 將6個參數分別賦值給該類內部的6個成員字段. 接下來我們就分別分析一下這6個參數各自的含義. 而要想正確理解這些參數以及對應字段的含義, 需要同時結合該構造方法的註釋以及對應字段的註釋, 對應字段的getter(), setter()方法的註釋才能基本無誤地理解透徹, 有時甚至還需要結合源碼才能真正理解. 下面我們來逐一分析這幾個參數(其實也就是分析6個成員字段的含義).

  • corePoolSize

將該類最前邊關於 Core and maximum pool sizes 和 On-demand construction 的註釋, 構造方法對該參數 corePoolSize 的註釋, 以及該類對同名字段 corePoolSize 的註釋彙總如下:

/**
 * <dt>Core and maximum pool sizes</dt>
 *
 * <dd>A {@code ThreadPoolExecutor} will automatically adjust the
 * pool size (see {@link #getPoolSize})
 * according to the bounds set by
 * corePoolSize (see {@link #getCorePoolSize}) and
 * maximumPoolSize (see {@link #getMaximumPoolSize}).
 *
 * When a new task is submitted in method {@link #execute(Runnable)},
 * and fewer than corePoolSize threads are running, a new thread is
 * created to handle the request, even if other worker threads are
 * idle.  If there are more than corePoolSize but less than
 * maximumPoolSize threads running, a new thread will be created only
 * if the queue is full.  By setting corePoolSize and maximumPoolSize
 * the same, you create a fixed-size thread pool. By setting
 * maximumPoolSize to an essentially unbounded value such as {@code
 * Integer.MAX_VALUE}, you allow the pool to accommodate an arbitrary
 * number of concurrent tasks. Most typically, core and maximum pool
 * sizes are set only upon construction, but they may also be changed
 * dynamically using {@link #setCorePoolSize} and {@link
 * #setMaximumPoolSize}. </dd>
 *
 * <dt>On-demand construction</dt>
 *
 * <dd>By default, even core threads are initially created and
 * started only when new tasks arrive, but this can be overridden
 * dynamically using method {@link #prestartCoreThread} or {@link
 * #prestartAllCoreThreads}.  You probably want to prestart threads if
 * you construct the pool with a non-empty queue. </dd>
 */

/**
 * Core pool size is the minimum number of workers to keep alive
 * (and not allow to time out etc) unless allowCoreThreadTimeOut
 * is set, in which case the minimum is zero.
 */
private volatile int corePoolSize;

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param corePoolSize the number of threads to keep in the pool, even
 *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
 * ...
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    // ...
    this.corePoolSize = corePoolSize;
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58

結合上述註釋可知, corePoolSize 字段表示的是線程池中一直存活着的線程的最小數量, 這些一直存活着的線程又被稱爲核心線程. 默認情況下, 核心線程的這個最小數量都是正數, 除非調用了allowCoreThreadTimeOut()方法並傳遞參數爲true, 設置允許核心線程因超時而停止(terminated), 在那種情況下, 一旦所有的核心線程都先後因超時而停止了, 將使得線程池中的核心線程數量最終變爲0, 也就是一直存活着的線程數爲0, 這將是那種情況下, 線程池中核心線程數量的最小值. 默認情況下, 核心線程是按需創建並啓動的, 也就是說, 只有當線程池接收到我們提交給他的任務後, 他纔會去創建並啓動一定數量的核心線程來執行這些任務. 如果他沒有接收到相關任務, 他就不會主動去創建核心線程. 這種默認的核心線程的創建啓動機制, 有助於降低系統資源的消耗. 變主動爲被動, 類似於常見的觀察者模式. 當然這只是系統默認的方式, 如果有特殊需求的話, 我們也可以通過調用 prestartCoreThread() 或 prestartAllCoreThreads() 方法來改變這一機制, 使得在新任務還未提交到線程池的時候, 線程池就已經創建並啓動了一個或所有核心線程, 並讓這些核心線程在池子裏等待着新任務的到來.

  • maximumPoolSize

將該類最前邊對 Core and maximum pool sizes 的註釋, 構造方法對該參數 maximumPoolSize 的註釋, 以及該類中的同名字段 maximumPoolSize 的註釋進行彙總如下:

/**
 * <dt>Core and maximum pool sizes</dt>
 *
 * <dd>A {@code ThreadPoolExecutor} will automatically adjust the
 * pool size (see {@link #getPoolSize})
 * according to the bounds set by
 * corePoolSize (see {@link #getCorePoolSize}) and
 * maximumPoolSize (see {@link #getMaximumPoolSize}).
 *
 * When a new task is submitted in method {@link #execute(Runnable)},
 * and fewer than corePoolSize threads are running, a new thread is
 * created to handle the request, even if other worker threads are
 * idle.  If there are more than corePoolSize but less than
 * maximumPoolSize threads running, a new thread will be created only
 * if the queue is full.  By setting corePoolSize and maximumPoolSize
 * the same, you create a fixed-size thread pool. By setting
 * maximumPoolSize to an essentially unbounded value such as {@code
 * Integer.MAX_VALUE}, you allow the pool to accommodate an arbitrary
 * number of concurrent tasks. Most typically, core and maximum pool
 * sizes are set only upon construction, but they may also be changed
 * dynamically using {@link #setCorePoolSize} and {@link
 * #setMaximumPoolSize}. </dd>
 */

/**
 * Maximum pool size. Note that the actual maximum is internally
 * bounded by CAPACITY.
 */
private volatile int maximumPoolSize;

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param maximumPoolSize the maximum number of threads to allow in the
 *        pool
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    // ...
    this.maximumPoolSize = maximumPoolSize;
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

maximumPoolSize 表示線程池內能夠容納線程數量的最大值. 當然, 線程數量的最大值還不能超過常量 CAPACITY 的數值大小, 根據如下的源碼, 我們很容易計算出 CAPACITY 的數值等於 (1 << 29 - 1), 也就是 1先左移29位然後再減1以後的數值, 在介紹 ctl 的時候曾經提到過, 這個數值約等於5億.

private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
  • 1
  • 2

所以, 我們設定的參數 maximumPoolSize 和 CAPACITY 二者中較小的那個數值纔是線程池中線程數量的最大值. 這裏多說一句, 如果我們提供的阻塞隊列 (也就是參數 workQueue) 是一個無界的隊列, 那麼這裏提供的 maximumPoolSize 的數值將毫無意義, 這一點我們會在後面解釋. 當我們通過方法 execute(Runnable) 提交一個任務到線程池時, 如果處於運行狀態(RUNNING)的線程數量少於核心線程數(corePoolSize), 那麼即使有一些非核心線程處於空閒等待狀態, 系統也會傾向於創建一個新的線程來處理這個任務. 如果此時處於運行狀態(RUNNING)的線程數量大於核心線程數(corePoolSize), 但又小於最大線程數(maximumPoolSize), 那麼系統將會去判斷線程池內部的阻塞隊列 workQueue 中是否還有空位子. 如果發現有空位子, 系統就會將該任務先存入該阻塞隊列; 如果發現隊列中已沒有空位子(即: 隊列已滿), 系統就會新創建一個線程來執行該任務.

如果將線程池的核心線程數 corePoolSize 和 最大線程數 maximumPoolSize 設置爲相同的數值(也就是說, 線程池中的所有線程都是核心線程), 那麼該線程池就是一個容量固定的線程池. 如果將最大線程數 maximumPoolSize 設置爲一個非常大的數值(例如: Integer.MAX_VALUE), 那麼就相當於允許線程池自己在不同時段去動態調整參與併發的任務總數. 通常情況下, 核心線程數 corePoolSize 和 最大線程數 maximumPoolSize 僅在創建線程池的時候纔去進行設定, 但是, 如果在線程池創建完成以後, 你又想去修改這兩個字段的值, 你就可以調用 setCorePoolSize() 和 setMaximumPoolSize() 方法來分別重新設定核心線程數 corePoolSize 和 最大線程數 maximumPoolSize 的數值.

  • keepAliveTime

將該類最前面關於 Keep-alive times 的註釋, 構造方法對參數keepAliveTime 的註釋, 以及字段 keepAliveTime 的註釋彙總如下:

/**
 * <dt>Keep-alive times</dt>
 *
 * <dd>If the pool currently has more than corePoolSize threads,
 * excess threads will be terminated if they have been idle for more
 * than the keepAliveTime (see {@link #getKeepAliveTime(TimeUnit)}).
 * This provides a means of reducing resource consumption when the
 * pool is not being actively used. If the pool becomes more active
 * later, new threads will be constructed. This parameter can also be
 * changed dynamically using method {@link #setKeepAliveTime(long,
 * TimeUnit)}.  Using a value of {@code Long.MAX_VALUE} {@link
 * TimeUnit#NANOSECONDS} effectively disables idle threads from ever
 * terminating prior to shut down. By default, the keep-alive policy
 * applies only when there are more than corePoolSize threads. But
 * method {@link #allowCoreThreadTimeOut(boolean)} can be used to
 * apply this time-out policy to core threads as well, so long as the
 * keepAliveTime value is non-zero. </dd>
 */

/**
 * Timeout in nanoseconds for idle threads waiting for work.
 * Threads use this timeout when there are more than corePoolSize
 * present or if allowCoreThreadTimeOut. Otherwise they wait
 * forever for new work.
 */
private volatile long keepAliveTime;

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param keepAliveTime when the number of threads is greater than
 *        the core, this is the maximum time that excess idle threads
 *        will wait for new tasks before terminating.
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    // ...
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

從註釋可知, keepAliveTime 表示空閒線程處於等待狀態的超時時間(也即, 等待時間的上限值, 超過該時間後該線程會停止工作). 當總線程數大於 corePoolSize (核心線程數) 並且 allowCoreThreadTimeOut 爲 false 時, 這些多出來的非核心線程一旦進入到空閒等待狀態, 就開始計算各自的等待時間, 並用這裏設定的 keepAliveTime 的數值作爲他們的超時時間, 一旦某個非核心線程的等待時間達到了超時時間, 該線程就會停止工作(terminated), 而核心線程在這種情況下卻不會受超時機制的制約, 核心線程即使等待的時間超出了這裏設定的 keepAliveTime, 也依然可以繼續處於空閒等待狀態而不會停止工作. 但是, 如果 allowCoreThreadTimeOut 被設置爲 true 並且設置的 keepAliveTime > 0, 那麼不論是非核心線程還是核心線程, 都將受超時機制的制約. 所以, 如果要執行的任務相對較多,並且每個任務執行的時間比較短,那麼可以爲該參數設置一個相對較大的數值,以提高線程的利用率。如果執行的任務相對較少, 線程池使用率相對較低, 那麼可以先將該參數設置爲一個較小的數值, 通過超時停止的機制來降低系統線程資源的開銷, 後續如果發現線程池的使用率逐漸增高以後, 線程池會根據當前提交的任務數自動創建新的線程, 當然, 我們也可以自己手動調用 setKeepAliveTime(long, TimeUnit)方法來重新設定 keepAliveTime 字段的值. 如果將 keepAliveTime 和 unit 這兩個參數分別設置爲 Long.MAX_VALUE 和 TimeUnit.NANOSECONDS(納秒), 這可以讓空閒線程基本上一直處於存活狀態. 因爲這兩個數值的組合表示設置超時時間約爲292年, 這是個非常非常長的時間了, 所以我們可以認爲這種設置基本等效於讓空閒線程一直處於存活狀態. 另外值得注意的是, 構造方法中的參數 keepAliveTime 的數值和字段 keepAliveTime 的數值很可能不同. 因爲參數 keepAliveTime 對應的時間單位可以是任意的, 這取決於另一個參數 unit 賦的是什麼值, 可選的值有納秒(NANOSECONDS), 微秒(MICROSECONDS), 毫秒(MILLISECONDS), 秒(SECONDS), 分鐘(MINUTES), 小時(HOURS), 天(DAYS). 而與其同名的字段 keepAliveTime 對應的時間單位則被強制要求是納秒. 所以, 構造方法會將參數 keepAliveTime 和 unit 二者組合起來的時間值換算成以納秒爲單位的數值, 並將換算後得到的數值作爲字段 keepAliveTime 的值. 注意這裏的參數 keepAliveTime 是 long 型而不是 int 型的, 因爲如果 keepAliveTime 爲 int 型, 並且爲 unit 賦的值是 NANOSECONDS(納秒), 那麼即使取 int 型的最大值 Integer.MAX_VALUE 作爲 keepAliveTime 的數值, 設置的超時時間就是 0x7FFFFFFF 納秒, 換算成秒就是2秒, 即: 這種情況下能夠設置的最大超時時間是2秒, 時間太短, 所以將參數 keepAliveTime 定義爲 long 型就保證了能夠設置的超時時間不至於太短.

  • workQueue

從源碼可知, 這裏的 workQueue 是一個 BlockingQueue(阻塞隊列) 的實例, 傳入的泛型類型是 Runnable. 也就是說, workQueue 是一個內部元素爲 Runnable(各種任務, 通常是異步的任務) 的阻塞隊列. 阻塞隊列是一種類似於 “生產者 - 消費者”模型的隊列. 當隊列已滿時如果繼續向隊列中插入元素, 該插入操作將被阻塞一直處於等待狀態, 直到隊列中有元素被移除產生空位子後, 纔有可能執行這次插入操作; 當隊列爲空時如果繼續執行元素的刪除或獲取操作, 該操作同樣會被阻塞而進入等待狀態, 直到隊列中又有了該元素後, 纔有可能執行該操作.

下面將該類最前面的註釋中關於 Queuing 的那部分註釋, 字段 workQueue 的註釋, 以及構造方法對參數 workQueue 的註釋三者彙總如下:

/**
 * <dt>Queuing</dt>
 *
 * Any {@link BlockingQueue} may be used to transfer and hold
 * submitted tasks.  The use of this queue interacts with pool sizing:
 *
 * If fewer than corePoolSize threads are running, the Executor
 * always prefers adding a new thread
 * rather than queuing.
 *
 * If corePoolSize or more threads are running, the Executor
 * always prefers queuing a request rather than adding a new
 * thread.
 *
 * If a request cannot be queued, a new thread is created unless
 * this would exceed maximumPoolSize, in which case, the task will be
 * rejected.
 *
 *
 * There are three general strategies for queuing:
 *
 * <em> Direct handoffs.</em> A good default choice for a work
 * queue is a {@link SynchronousQueue} that hands off tasks to threads
 * without otherwise holding them. Here, an attempt to queue a task
 * will fail if no threads are immediately available to run it, so a
 * new thread will be constructed. This policy avoids lockups when
 * handling sets of requests that might have internal dependencies.
 * Direct handoffs generally require unbounded maximumPoolSizes to
 * avoid rejection of new submitted tasks. This in turn admits the
 * possibility of unbounded thread growth when commands continue to
 * arrive on average faster than they can be processed.
 *
 * <em> Unbounded queues.</em> Using an unbounded queue (for
 * example a {@link LinkedBlockingQueue} without a predefined
 * capacity) will cause new tasks to wait in the queue when all
 * corePoolSize threads are busy. Thus, no more than corePoolSize
 * threads will ever be created. (And the value of the maximumPoolSize
 * therefore doesn't have any effect.)  This may be appropriate when
 * each task is completely independent of others, so tasks cannot
 * affect each others execution; for example, in a web page server.
 * While this style of queuing can be useful in smoothing out
 * transient bursts of requests, it admits the possibility of
 * unbounded work queue growth when commands continue to arrive on
 * average faster than they can be processed.
 *
 * <em>Bounded queues.</em> A bounded queue (for example, an
 * {@link ArrayBlockingQueue}) helps prevent resource exhaustion when
 * used with finite maximumPoolSizes, but can be more difficult to
 * tune and control.  Queue sizes and maximum pool sizes may be traded
 * off for each other: Using large queues and small pools minimizes
 * CPU usage, OS resources, and context-switching overhead, but can
 * lead to artificially low throughput.  If tasks frequently block (for
 * example if they are I/O bound), a system may be able to schedule
 * time for more threads than you otherwise allow. Use of small queues
 * generally requires larger pool sizes, which keeps CPUs busier but
 * may encounter unacceptable scheduling overhead, which also
 * decreases throughput.
 */

/**
 * The queue used for holding tasks and handing off to worker
 * threads.  We do not require that workQueue.poll() returning
 * null necessarily means that workQueue.isEmpty(), so rely
 * solely on isEmpty to see if the queue is empty (which we must
 * do for example when deciding whether to transition from
 * SHUTDOWN to TIDYING).  This accommodates special-purpose
 * queues such as DelayQueues for which poll() is allowed to
 * return null even if it may later return non-null when delays
 * expire.
 */
private final BlockingQueue<Runnable> workQueue;

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param workQueue the queue to use for holding tasks before they are
 *        executed.  This queue will hold only the {@code Runnable}
 *        tasks submitted by the {@code execute} method.
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    // ...
    this.workQueue = workQueue;
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91

workQueue 是一個用於保存等待執行的任務的阻塞隊列. 當提交一個新的任務到線程池以後, 線程池會根據當前池子中正在運行着的線程的數量, 指定出對該任務相應的處理方式, 主要有以下幾種處理方式: 
(1) 如果線程池中正在運行的線程數少於核心線程數, 那麼線程池總是傾向於創建一個新線程來執行該任務, 而不是將該任務提交到該隊列 workQueue 中進行等待. 
(2) 如果線程池中正在運行的線程數不少於核心線程數, 那麼線程池總是傾向於將該任務先提交到隊列 workQueue 中先讓其等待, 而不是創建一個新線程來執行該任務. 
(3) 如果線程池中正在運行的線程數不少於核心線程數, 並且線程池中的阻塞隊列也滿了使得該任務入隊失敗, 那麼線程池會去判斷當前池子中運行的線程數是否已經等於了該線程池允許運行的最大線程數 maximumPoolSize. 如果發現已經等於了, 說明池子已滿, 無法再繼續創建新的線程了, 那麼就會拒絕執行該任務. 如果發現運行的線程數小於池子允許的最大線程數, 那麼就會創建一個線程(這裏創建的線程是非核心線程)來執行該任務.

一般來說, 隊列對新提交的任務有三種常見的處理策略: 
(1) 直接切換. 常用的隊列是 SynchronousQueue (同步隊列). 這種隊列內部不會存儲元素. 每一次插入操作都會先進入阻塞狀態, 一直等到另一個線程執行了刪除操作, 然後該插入操作纔會執行. 同樣地, 每一次刪除操作也都會先進入阻塞狀態, 一直等到另一個線程執行了插入操作, 然後該刪除操作纔會執行. 當提交一個任務到包含這種 SynchronousQueue 隊列的線程池以後, 線程池會去檢測是否有可用的空閒線程來執行該任務, 如果沒有就直接新建一個線程來執行該任務而不是將該任務先暫存在隊列中. “直接切換”的意思就是, 處理方式由”將任務暫時存入隊列”直接切換爲”新建一個線程來處理該任務”. 這種策略適合用來處理多個有相互依賴關係的任務, 因爲該策略可以避免這些任務因一個沒有及時處理而導致依賴於該任務的其他任務也不能及時處理而造成的鎖定效果. 因爲這種策略的目的是要讓幾乎每一個新提交的任務都能得到立即處理, 所以這種策略通常要求最大線程數 maximumPoolSizes 是無界的(即: Integer.MAX_VALUE). 靜態工廠方法 Executors.newCachedThreadPool() 使用了這個隊列。 
(2) 使用無界隊列 (也就是不預設隊列的容量, 隊列將使用 Integer.MAX_VALUE 作爲其默認容量, 例如: 基於鏈表的阻塞隊列 LinkedBlockingQueue). 使用無界隊列將使得線程池中能夠創建的最大線程數就等於核心線程數 corePoolSize, 這樣線程池的 maximumPoolSize 的數值起不到任何作用. 如果向這種線程池中提交一個新任務時發現所有核心線程都處於運行狀態, 那麼該任務將被放入無界隊列中等待處理. 當要處理的多個任務之間沒有任何相互依賴關係時, 就適合使用這種隊列策略來處理這些任務. 靜態工廠方法 Executors.newFixedThreadPool() 使用了這個隊列。 
(3) 使用有界隊列 (例如: 基於數組的阻塞隊列 ArrayBlockingQueue). 當要求線程池的最大線程數 maximumPoolSizes 要限定在某個值以內時, 線程池使用有界隊列能夠降低資源的消耗, 但這也使得線程池對線程的調控變得更加困難. 因爲隊列容量和線程池容量都是有限的值, 要想使線程處理任務的吞吐量能夠在一個相對合理的範圍內, 同時又能使線程調配的難度相對較低, 並且又儘可能節省系統資源的消耗, 那麼就需要合理地調配這兩個數值. 通常來說, 設置較大的隊列容量和較小的線程池容量, 能夠降低系統資源的消耗(包括CPU的使用率, 操作系統資源的消耗, 上下文環境切換的開銷等), 但卻會降低線程處理任務的吞吐量. 如果發現提交的任務經常頻繁地發生阻塞的情況, 那麼你就可以考慮增大線程池的容量, 可以通過調用 setMaximumPoolSize() 方法來重新設定線程池的容量. 而設置較小的隊列容量時, 通常需要將線程池的容量設置大一點, 這種情況下, CPU的使用率會相對較高, 當然如果線程池的容量設置過大的話, 可能會有非常非常多的線程來同時處理提交來的多個任務, 併發數過大時, 線程之間的調度將會是個非常嚴峻的問題, 這反而有可能降低任務處理的吞吐量, 出現過猶不及的局面.

  • threadFactory

將該類前邊關於該參數的那部分註釋 “Creating new threads”, 構造方法對該參數的註釋, 以及該類對字段 threadFactory 的註釋彙總如下:

/** <dt>Creating new threads</dt>
 *
 * <dd>New threads are created using a {@link ThreadFactory}.  If not
 * otherwise specified, a {@link Executors#defaultThreadFactory} is
 * used, that creates threads to all be in the same {@link
 * ThreadGroup} and with the same {@code NORM_PRIORITY} priority and
 * non-daemon status. By supplying a different ThreadFactory, you can
 * alter the thread's name, thread group, priority, daemon status,
 * etc. If a {@code ThreadFactory} fails to create a thread when asked
 * by returning null from {@code newThread}, the executor will
 * continue, but might not be able to execute any tasks.</dd>
 */

/**
 * Factory for new threads. All threads are created using this
 * factory (via method addWorker).  All callers must be prepared
 * for addWorker to fail, which may reflect a system or user's
 * policy limiting the number of threads.  Even though it is not
 * treated as an error, failure to create threads may result in
 * new tasks being rejected or existing ones remaining stuck in
 * the queue.
 *
 * We go further and preserve pool invariants even in the face of
 * errors such as OutOfMemoryError, that might be thrown while
 * trying to create threads.  Such errors are rather common due to
 * the need to allocate a native stack in Thread.start, and users
 * will want to perform clean pool shutdown to clean up.  There
 * will likely be enough memory available for the cleanup code to
 * complete without encountering yet another OutOfMemoryError.
 */
private volatile ThreadFactory threadFactory;

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param threadFactory the factory to use when the executor
 *        creates a new thread
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    // ...
    this.threadFactory = threadFactory;
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

threadFactory 線程工廠, 用於創建線程. 如果我們在創建線程池的時候未指定該 threadFactory 參數, 線程池則會使用 Executors.defaultThreadFactory() 方法創建默認的線程工廠. 如果我們想要爲線程工廠創建的線程設置一些特殊的屬性, 例如: 設置見名知意的名字, 設置特定的優先級等等, 那麼我們就需要自己去實現 ThreadFactory 接口, 並在實現其抽象方法 newThread()的時候, 使用Thread類包含 threadName (線程名字)的那個構造方法就可以指定線程的名字(通常可以指定見名知意的名字), 還可以用 setPriority() 方法爲線程設置特定的優先級等. 然後在創建線程池的時候, 將我們自己實現的 ThreadFactory 接口的實現類對象作爲 threadFactory 參數的值傳遞給線程池的構造方法即可.

  • handler

將該類前邊關於該參數的那部分註釋 “Rejected tasks”, 構造方法對該參數的註釋, 以及該類對字段 handler 的註釋彙總如下:

/** 
 * <dt>Rejected tasks</dt>
 *
 * <dd>New tasks submitted in method {@link #execute(Runnable)} will be
 * <em>rejected</em> when the Executor has been shut down, and also when
 * the Executor uses finite bounds for both maximum threads and work queue
 * capacity, and is saturated.  In either case, the {@code execute} method
 * invokes the {@link
 * RejectedExecutionHandler#rejectedExecution(Runnable, ThreadPoolExecutor)}
 * method of its {@link RejectedExecutionHandler}.  Four predefined handler
 * policies are provided:
 *
 * <ol>
 *
 * <li> In the default {@link ThreadPoolExecutor.AbortPolicy}, the
 * handler throws a runtime {@link RejectedExecutionException} upon
 * rejection. </li>
 *
 * <li> In {@link ThreadPoolExecutor.CallerRunsPolicy}, the thread
 * that invokes {@code execute} itself runs the task. This provides a
 * simple feedback control mechanism that will slow down the rate that
 * new tasks are submitted. </li>
 *
 * <li> In {@link ThreadPoolExecutor.DiscardPolicy}, a task that
 * cannot be executed is simply dropped.  </li>
 *
 * <li>In {@link ThreadPoolExecutor.DiscardOldestPolicy}, if the
 * executor is not shut down, the task at the head of the work queue
 * is dropped, and then execution is retried (which can fail again,
 * causing this to be repeated.) </li>
 *
 * </ol>
 *
 * It is possible to define and use other kinds of {@link
 * RejectedExecutionHandler} classes. Doing so requires some care
 * especially when policies are designed to work only under particular
 * capacity or queuing policies. </dd>
 */

/**
 * Handler called when saturated or shutdown in execute.
 */
private volatile RejectedExecutionHandler handler;

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param handler the handler to use when execution is blocked
 *        because the thread bounds and queue capacities are reached
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    // ...
    this.handler = handler;
    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62

根據註釋可知, 以下兩個條件滿足其中任意一個的時候, 如果繼續向該線程池中提交新的任務, 那麼線程池將會調用他內部的 RejectedExecutionHandler 對象(也就是 handler)的 rejectedExecution()方法, 表示拒絕執行這些新提交的任務:

① 當線程池處於 SHUTDOWN (關閉) 狀態時 (不論線程池和阻塞隊列是否都已滿) 
② 當線程池中的所有線程都處於運行狀態並且線程池中的阻塞隊列已滿時

可能只是用文字描述不太容易理解, 並且印象也不深刻, 下面我們寫個簡單的 demo來給大家展示一下這兩種情況到底是什麼意思:

package com.example.thread_pool_executor_test;

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 線程池觸發 RejectedExecutionHandler拒絕新提交任務的場景模擬 demo
 *
 * @author zhangzhiyi
 * @version 1.0
 * @createTime 2016/2/25 13:53
 * @projectName ThreadPoolExecutorTest
 */
public class ThreadPoolExecutorRejectNewTaskDemo {

    // 線程池的最大容量
    private static final int MAX_POOL_SIZE = 3;
    // 阻塞隊列的容量
    private static final int QUEUE_CAPACITY = 2;
    // 非核心線程處於空閒狀態的最長時間
    private static final int KEEP_ALIVE_TIME_VALUE = 1;
    // 線程池對象
    private static final ThreadPoolExecutor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(
            MAX_POOL_SIZE, MAX_POOL_SIZE,
            KEEP_ALIVE_TIME_VALUE, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(QUEUE_CAPACITY),
            new MyThreadFactory());

    public static void main(String[] args) {
        // 模擬線程池及其內部的隊列都已滿後, 繼續向其提交新任務將會被拒絕的場景
//        threadPoolFullToRejectNewTask();

        // 模擬線程池被關閉(shutdown)後, 繼續向其提交新任務將會被拒絕的場景
        shutdownThreadPoolToRejectNewTask();
    }

    /**
     * 模擬線程池被關閉(shutdown)後, 繼續向其提交新任務將會被拒絕的場景
     */
    private static void shutdownThreadPoolToRejectNewTask() {
        MyRunnable r = new MyRunnable();

        int cycleCount = Math.max(MAX_POOL_SIZE - 1, 0);

        // 先提交(MAX_POOL_SIZE - 1)個任務. 顯然, 線程池此時還未滿
        for (int i = 0; i < cycleCount; i++) {
            System.out.println("提交任務" + i);
            THREAD_POOL_EXECUTOR.execute(r);
        }
        // 在線程池未滿的情況下關閉線程池.
        THREAD_POOL_EXECUTOR.shutdown();

        // 在線程池已處於關閉(SHUTDOWN)的狀態下
        if (THREAD_POOL_EXECUTOR.isShutdown()) {
            try {
                System.out.println("提交任務" + cycleCount);
                Thread.sleep(10);
                // 在線程池未滿但卻已經關閉了的情況下, 繼續向該線程池中提交任務.
                THREAD_POOL_EXECUTOR.execute(r);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 模擬線程池及其內部的隊列都已滿後, 繼續向其提交新任務將會被拒絕的場景
     */
    private static void threadPoolFullToRejectNewTask() {
        MyRunnable r = new MyRunnable();
        // 循環提交任務的總次數. 該總次數等於"線程池的最大線程容量和阻塞隊列的容量之和", 在執行完
        // 該循環後, 線程池和阻塞隊列都已滿.
        int cycleCount = MAX_POOL_SIZE + QUEUE_CAPACITY;

        for (int i = 0; i < cycleCount; i++) {
            System.out.println("提交任務" + i);
            THREAD_POOL_EXECUTOR.execute(r);
        }
        // 當前已提交的任務數
        int tasksCount = cycleCount;

        // 在線程池和阻塞隊列都已滿的情況下, 繼續提交任務.
        try {
            System.out.println("提交任務" + (tasksCount));
            Thread.sleep(10);
            THREAD_POOL_EXECUTOR.execute(r);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /**
     * 自定義的線程工廠類, 用於爲線程池創建線程對象.
     */
    private static class MyThreadFactory implements ThreadFactory {
        static int threadNumber = 0;

        @Override
        public Thread newThread(Runnable r) {
            String threadName = "thread-" + (threadNumber++);
            System.out.println("創建線程 " + threadName);
            return new Thread(r, threadName);
        }
    }

    /**
     * 表示向線程池提交的任務的 Runnable實現類
     */
    private static class MyRunnable implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121

在上邊的demo中, 我們定義了一個最大線程數爲3, 阻塞隊列容量爲2的線程池, 並且我們使用的是不帶 RejectedExecutionHandler 參數的那個構造方法. 我們將上述兩個條件結合到我們這個demo中來說就是: 
① 當線程池的 isShutdown() 方法返回 true 的時候 (即使此時線程池中處於運行狀態的線程數少於3個, 或者阻塞隊列中的元素個數少於2個, 也會拒絕新提交的任務) 
② 當線程池中處於運行狀態的線程數爲3個, 並且阻塞隊列中包含的元素個數等於2個的時候, 繼續提交的任務就會被拒絕.

我們定義了兩個方法 shutdownThreadPoolToRejectNewTask() 和 threadPoolFullToRejectNewTask(), 來模擬在分別滿足上邊兩個條件的情況下, 向線程池中繼續提交任務的情景.

在方法 shutdownThreadPoolToRejectNewTask() 中, 我們先提交了 (MAX_POOL_SIZE - 1) 個任務, 也就是2個任務, 此時線程池還未滿, 阻塞隊列還是空的, 我們調用線程池的 shutdown() 方法 (也可調用 shutdownNow()方法) 將線程池關閉, 並且爲了確保線程池已經關閉, 我們又調用了線程池的 isShutdown()方法來判斷線程池是否已經關閉了, 在該方法返回 true的時候, 也就是說, 在確認線程池已經處於關閉(SHUTDOWN) 狀態時, 我們向該線程池中又提交了一個任務 (見代碼第61行), 也就是第3個任務, 我們來看看此時會發生什麼情況呢? 下面是執行過程所打印的 log信息: 
這裏寫圖片描述
從log信息可以看出, 當我們提交第3個任務 (即: 任務2) 的時候, 線程池直接拋出了異常, 並且導致異常發生的代碼就是61行的 THREAD_POOL_EXECUTOR.execute(r); 這句代碼. 所以, 線程池拒絕接受新任務的處理方式就是直接拋異常 (其實, 在看完後邊的介紹後, 你就會知道, 拋異常只是其中的一種處理方式, 也是線程池默認的處理方式, 線程池還爲我們提供了其他幾種處理方式, 當然我們自己也可以提供自定義的處理方式).

我們再來看看另一個方法 threadPoolFullToRejectNewTask() 所模擬的場景. 在該方法中, 我們一共提交了 (MAX_POOL_SIZE + QUEUE_CAPACITY + 1) 個任務, 也就是6個任務, 而線程池的最大線程數和核心線程數都爲3, 所以在提交前3個任務 (即: 任務0, 任務1, 任務2) 後, 線程池會分別創建3個核心線程 (此時線程池已滿)來處理這3個任務, 接下來我們再提交的任務就會被暫時存入阻塞隊列中, 而我們爲阻塞隊列設定的容量爲2, 所以我們接下來提交的第4個和第5個任務 (即: 任務3和任務4), 都將存入阻塞隊列中 (此時阻塞隊列也滿了), 這時已經滿足了前邊提到的條件②, 而我們一共要提交6個任務, 所以當我們繼續提交第6個任務 (也就是任務5, 見代碼第88行) 時, 同樣也會被拒絕, 而拒絕的方式也是拋出異常, 見如下的 log 信息: 
這裏寫圖片描述
從 log 也可以看出, 正是第88行提交的第6個任務, 導致了異常的拋出. 當然, 拋異常也同樣是這種情況下線程池默認的處理方式, 我們也可以改爲使用線程池提供的其他處理方式, 或者我們自己提供自定義的處理方式.

在上面的demo例子中, 我們對拒絕新任務的處理方式有了一個較爲直觀的認識, 我們知道了默認情況下線程池是使用拋異常的方式來拒絕新提交的任務的, 這種拋異常的方式在線程池中被稱爲 AbortPolicy. 當然, 除了這種 AbortPolicy 方式外, 線程池還爲我們提供了 CallerRunsPolicy, DiscardPolicy和 DiscardOldestPolicy 的方式, 下面我們就來分別簡要介紹下這幾種方式:

① AbortPolicy:

前面已經介紹過, 這是一種直接拋異常的處理方式, 拋出 RejectedExecutionException 異常. 如果在 ThreadPoolExecutor 的構造方法中未指定 RejectedExecutionHandler 參數, 那麼線程池將使用他內部預定義的 defaultHandler 這個字段作爲該參數的值, 而這個 defaultHandler 就是採用的 AbortPolicy 拋異常的方式 . 這也就解釋了爲什麼在前邊的demo例子中, 線程池在滿足前邊提到的兩個條件中的任意一個時, 都會採取拋異常的方式來拒絕新提交的任務. 另外, 在Android開發中, 我們常用的 AsyncTask 類中也有個已定義好的線程池對象 THREAD_POOL_EXECUTOR, 而這個線程池同樣採用的是 AbortPolicy 拋異常的方式來拒絕新任務, 拋異常會導致我們的 Android APP 直接 crash 掉, 這是一種非常糟糕的用戶體驗. 所以我們在Android開發中要使用 AsyncTask 結合線程池來併發處理異步任務, 如果併發執行的任務數較多的話, 建議不要直接使用 AsyncTask 內部自帶的那個線程池, 而應該自定義一個線程池對象, 併爲 RejectedExecutionHandler 參數賦予一個能夠提供更友好處理方式的實現類對象.

② CallerRunsPolicy:

將新提交的任務放在 ThreadPoolExecutor.execute()方法所在的那個線程中執行.

③ DiscardPolicy:

直接不執行新提交的任務.

④ DiscardOldestPolicy
爲避免片面理解, 我們有必要看下他的源碼:

/**
 * A handler for rejected tasks that discards the oldest unhandled
 * request and then retries {@code execute}, unless the executor
 * is shut down, in which case the task is discarded.
 */
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    /**
     * Creates a {@code DiscardOldestPolicy} for the given executor.
     */
    public DiscardOldestPolicy() { }

    /**
     * Obtains and ignores the next task that the executor
     * would otherwise execute, if one is immediately available,
     * and then retries execution of task r, unless the executor
     * is shut down, in which case task r is instead discarded.
     *
     * @param r the runnable task requested to be executed
     * @param e the executor attempting to execute this task
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

結合註釋和源碼可知, 這種處理方式分爲兩種情況: 
① 當線程池已經關閉 (SHUTDOWN) 時, 就不執行這個任務了, 這也是 DiscardPolicy 的處理方式. 
② 當線程池未關閉時, 會將阻塞隊列中處於隊首 (head) 的那個任務從隊列中移除, 然後再將這個新提交的任務加入到該阻塞隊列的隊尾 (tail) 等待執行.

以上就是對這四種處理方式的簡要介紹. 但是, 你可千萬不要認爲就只能在這四種方式中挑選一種, 因爲這四種方式只是線程池預定義的四種常用方式, 而 RejectedExecutionHandler 這個參數其實是個接口, 線程池所提供的這四種方式其實都是該接口的實現類, 所以, 只要我們自定義一個類來實現該接口, 並在重寫該接口的 rejectedExecution() 方法時提供我們自己的處理邏輯, 那麼我們就可以將我們自定義的這個類的對象作爲參數傳遞給線程池的構造方法. 當線程池滿足拒絕新任務的條件時, 如果我們繼續向其提交新任務, 那麼線程池就會採用我們自己提供的那套邏輯來處理這些新提交的任務了.

3. 線程池進行任務調度的原理

我們只能向線程池提交任務, 而被提交的任務最終能否執行以及能否立即執行, 則都由線程池自己來控制, 至於怎麼控制就涉及到線程池對任務調度的原理了. 我們要向線程池提交一個任務, 可以通過調用 execute() 或 submit()方法來實現, 而二者的區別是, execute()方法只能進行任務的提交而不能獲取該任務執行的結果, 但 submit()方法則既能進行任務的提交, 又能獲取該任務執行的結果. 所以, 如果你需要獲取一個任務執行的結果或者需要對一個任務執行的結果進行某種操作, 那麼就需要使用 submit()方法來提交任務. 其實 submit()方法就是對 execute()方法的一種封裝, 它內部也是調用 execute()方法來實現任務提交的, 只是因爲 submit()方法的返回值是一個 Future 對象, 通過返回的 Future對象就能獲取該任務最終執行的結果. 由於我們這裏介紹的主題是線程池對任務調度的原理, 而任務調度, 用較爲通俗的話來說, 就是一個任務被提交到線程池後能否被執行, 如果能被執行, 那麼是立即執行, 還是在未來的某個時刻去執行, 用哪個線程執行; 如果不能被執行, 那麼又將怎麼處理這個任務等等. 而這些既與前面介紹過的線程池中那幾個重要的參數的設置有關, 還與任務被提交的時刻有關(準確來說, 就是與該任務被提交時, 線程池內已有任務的情況有關). 所以, 我們有必要從線程的提交開始分析, 由於 submit()方法內部也是調用 execute()方法, 所以我們就直接分析 execute()方法, 其源碼如下: 

/**
 * Executes the given task sometime in the future.  The task
 * may execute in a new thread or in an existing pooled thread.
 *
 * If the task cannot be submitted for execution, either because this
 * executor has been shutdown or because its capacity has been reached,
 * the task is handled by the current {@code RejectedExecutionHandler}.
 *
 * @param command the task to execute
 * @throws RejectedExecutionException at discretion of
 *         {@code RejectedExecutionHandler}, if the task
 *         cannot be accepted for execution
 * @throws NullPointerException if {@code command} is null
 */
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */

    // 獲取ctl的值.
    int c = ctl.get();


    /********************************* 情況1 ************************************/

    // 根據ctl的值, 獲取線程池中的有效線程數 workerCount, 如果 workerCount
    // 小於核心線程數 corePoolSize
    if (workerCountOf(c) < corePoolSize) {

        // 調用addWorker()方法, 將核心線程數corePoolSize設置爲線程池中線程
        // 數的上限值, 將此次提交的任務command作爲參數傳遞進去, 然後再次獲取
        // 線程池中的有效線程數 workerCount, 如果 workerCount依然小於核心
        // 線程數 corePoolSize, 就創建並啓動一個線程, 然後返回 true結束整個
        // execute()方法. 如果此時的線程池已經關閉, 或者此時再次獲取到的有
        // 效線程數 workerCount已經 >= 核心線程數 corePoolSize, 就再繼續執
        // 行後邊的內容. 
        if (addWorker(command, true))
            return;

        // 再次獲取 ctl的值
        c = ctl.get();
    }

    /***** 分析1 ****/
    // 如果情況1的判斷條件不滿足, 則直接進入情況2. 如果情況1的判斷條件滿足, 
    // 但情況1中的 addWorker()方法返回 false, 也同樣會進入情況2.  
    // 總之, 進入情況2時, 線程池要麼已經不處於RUNNING(運行)狀態, 要麼仍處於RUNNING
    // (運行)狀態但線程池內的有效線程數 workerCount >= 核心線程數 corePoolSize


    /********************************* 情況2 ************************************/

    /***** 分析2 ****/
    // 經過上一段分析可知, 進入這個情況時, 線程池要麼已經不處於RUNNING(運行)
    // 狀態, 要麼仍處於RUNNING(運行)狀態但線程池內的有效線程數 workerCount
    // 已經 >= 核心線程數 corePoolSize

    // 如果線程池未處於RUNNING(運行)狀態, 或者雖然處於RUNNING(運行)狀態但線程池
    // 內的阻塞隊列 workQueue已滿, 則跳過此情況直接進入情況3.
    // 如果線程池處於RUNNING(運行)狀態並且線程池內的阻塞隊列 workQueue未滿, 
    // 則將提交的任務 command 添加到阻塞隊列 workQueue中.
    if (isRunning(c) && workQueue.offer(command)) {
        // 再次獲取 ctl的值.
        int recheck = ctl.get();

        // 再次判斷線程池此時的運行狀態. 如果發現線程池未處於 RUNNING(運行)
        // 狀態, 由於先前已將任務 command加入到阻塞隊列 workQueue中了, 所以需
        // 要將該任務從 workQueue中移除. 一般來說, 該移除操作都能順利進行. 
        // 所以一旦移除成功, 就再調用 handler的 rejectedExecution()方法, 根據
        // 該 handler定義的拒絕策略, 對該任務進行處理. 當然, 默認的拒絕策略是
        // AbortPolicy, 也就是直接拋出 RejectedExecutionException 異常, 同時也
        // 結束了整個 execute()方法的執行.
        if (! isRunning(recheck) && remove(command))
            reject(command);

        // 再次計算線程池內的有效線程數 workerCount, 一旦發現該數量變爲0, 
        // 就將線程池內的線程數上限值設置爲最大線程數 maximumPoolSize, 然後
        // 只是創建一個線程而不去啓動它, 並結束整個 execute()方法的執行.
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);

        // 如果線程池處於 RUNNING(運行)狀態並且線程池內的有效線程數大於0, 那麼就直接結束該 
        // execute()方法, 被添加到阻塞隊列中的該任務將會在未來的某個時刻被執行.
    }


    /********************************* 情況3 ************************************/

    /***** 分析3 ****/
    // 如果該方法能夠執行到這裏, 那麼結合分析1和分析2可知, 線程池此時必定是
    // 下面兩種情況中的一種:
    // ① 已經不處於RUNNING(運行)狀態
    // ② 處於RUNNING(運行)狀態, 並且線程池內的有效線程數 workerCount已經
    //   >= 核心線程數 corePoolSize, 並且線程池內的阻塞隊列 workQueue已滿

    // 再次執行addWorker() 方法, 將線程池內的線程數上限值設置爲最大線程數 
    // maximumPoolSize, 並將提交的任務 command作爲被執行的對象, 嘗試創建並
    // 啓動一個線程來執行該任務. 如果此時線程池的狀態爲如下兩種中的一種, 
    // 就會觸發 handler的 rejectedExecution()方法來拒絕該任務的執行:
    // ① 未處於RUNNING(運行)狀態.
    // ② 處於RUNNING(運行)狀態, 但線程池內的有效線程數已達到本次設定的最大
    // 線程數 (另外根據分析3可知, 此時線程池內的阻塞隊列 workQueue已滿).
    //
    // 如果線程池處於 RUNNING(運行)狀態, 但有效線程數還未達到本次設定的最大
    // 線程數, 那麼就會嘗試創建並啓動一個線程來執行任務 command. 如果線程的
    // 創建和啓動都很順利, 那麼就直接結束掉該 execute()方法; 如果線程的創建或
    // 啓動失敗, 則同樣會觸發 handler的 rejectedExecution()方法來拒絕該
    // 任務的執行並結束掉該 execute()方法.
    else if (!addWorker(command, false))
        reject(command);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130

代碼中的註釋非常詳細, 這裏再簡要概括一下. execute()方法主要分爲以下四種情況: 
情況1: 如果線程池內的有效線程數少於核心線程數 corePoolSize, 那麼就創建並啓動一個線程來執行新提交的任務. 
情況2: 如果線程池內的有效線程數達到了核心線程數 corePoolSize, 並且線程池內的阻塞隊列未滿, 那麼就將新提交的任務加入到該阻塞隊列中. 
情況3: 如果線程池內的有效線程數達到了核心線程數 corePoolSize 但卻小於最大線程數 maximumPoolSize, 並且線程池內的阻塞隊列已滿, 那麼就創建並啓動一個線程來執行新提交的任務. 
情況4: 如果線程池內的有效線程數達到了最大線程數 maximumPoolSize, 並且線程池內的阻塞隊列已滿, 那麼就讓 RejectedExecutionHandler 根據它的拒絕策略來處理該任務, 默認的處理方式是直接拋異常.

上述四種情況可以使用下面的流程圖來描述 (這裏就直接借用方騰飛老師在 聊聊併發(三)——JAVA線程池的分析和使用 這篇文章裏的流程圖. 若侵刪): 
這裏寫圖片描述

上述 execute()源碼在三種情況下分別調用了 addWorker()方法, 並且三次傳遞的參數都不同 (見第 56, 99, 128行):

addWorker(command, true);
addWorker(null, false);
addWorker(command, false)
  • 1
  • 2
  • 3

所以, 要想徹底看懂 execute()方法的邏輯, 就必須要先大致瞭解 addWorker()方法的邏輯以及該方法分別在上述三組賦值情況下各自到底做了什麼事情. 下面我們還是來分析一下 addWorker()方法的源碼吧: 

/**
 * Checks if a new worker can be added with respect to current
 * pool state and the given bound (either core or maximum). If so,
 * the worker count is adjusted accordingly, and, if possible, a
 * new worker is created and started, running firstTask as its
 * first task. This method returns false if the pool is stopped or
 * eligible to shut down. It also returns false if the thread
 * factory fails to create a thread when asked.  If the thread
 * creation fails, either due to the thread factory returning
 * null, or due to an exception (typically OutOfMemoryError in
 * Thread.start()), we roll back cleanly.
 *
 * @param firstTask the task the new thread should run first (or
 * null if none). Workers are created with an initial first task
 * (in method execute()) to bypass queuing when there are fewer
 * than corePoolSize threads (in which case we always start one),
 * or when the queue is full (in which case we must bypass queue).
 * Initially idle threads are usually created via
 * prestartCoreThread or to replace other dying workers.
 *
 * @param core if true use corePoolSize as bound, else
 * maximumPoolSize. (A boolean indicator is used here rather than a
 * value to ensure reads of fresh values after checking other pool
 * state).
 * @return true if successful
 */
private boolean addWorker(Runnable firstTask, boolean core) {

    // retry 是個無限循環. 當線程池處於 RUNNING (運行)狀態時, 只有在線程池中
    // 的有效線程數被成功加一以後, 纔會退出該循環而去執行後邊的代碼. 也就是說,
    // 當線程池在 RUNNING (運行)狀態下退出該 retry 循環時, 線程池中的有效線程數
    // 一定少於此次設定的最大線程數(可能是 corePoolSize 或 maximumPoolSize).
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 線程池滿足如下條件中的任意一種時, 就會直接結束該方法, 並且返回 false
        // 表示沒有創建新線程, 新提交的任務也沒有被執行.
        // ① 處於 STOP, TYDING 或 TERMINATD 狀態
        // ② 處於 SHUTDOWN 狀態, 並且參數 firstTask != null
        // ③ 處於 SHUTDOWN 狀態, 並且阻塞隊列 workQueue爲空

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);

            // 如果線程池內的有效線程數大於或等於了理論上的最大容量 CAPACITY 或者實際
            // 設定的最大容量, 就返回 false直接結束該方法. 這樣同樣沒有創建新線程, 
            // 新提交的任務也同樣未被執行.
            // (core ? corePoolSize : maximumPoolSize) 表示如果 core爲 true,
            // 那麼實際設定的最大容量爲 corePoolSize, 反之則爲 maximumPoolSize.
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;

            // 有效線程數加一
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 根據參數 firstTask來創建 Worker對象 w.
        w = new Worker(firstTask);
        // 用 w創建線程對象 t.
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {

                // 啓動線程 t. 由於 t指向 w.thread所引用的對象, 所以相當於啓動的是 w.thread所引用的線程對象.
                // 而 w是 Runnable 的實現類, w.thread 是以 w作爲 Runnable參數所創建的一個線程對象, 所以啓動
                // w.thread所引用的線程對象, 也就是要執行 w 的 run()方法.            
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117

這裏我們只分析線程池處於 RUNNING(運行)狀態時的情況, 並且只分析上述方法中核心的重要的代碼, 至於其他情況以及其他行的代碼, 就不分析了.

先看第78和80行, 根據參數 firstTask來創建 Worker對象 w, 並用 w再創建線程對象 t, t 指向 w.thread所指向的線程對象. 既然這裏提到了 Worker 類, 那麼我們就來簡要介紹一下這個類. Worker是 ThreadPoolExecutor類的一個內部類, 同時也是 Runnable接口的實現類, 其源碼如下: 

/**
 * Class Worker mainly maintains interrupt control state for
 * threads running tasks, along with other minor bookkeeping.
 * This class opportunistically extends AbstractQueuedSynchronizer
 * to simplify acquiring and releasing a lock surrounding each
 * task execution.  This protects against interrupts that are
 * intended to wake up a worker thread waiting for a task from
 * instead interrupting a task being run.  We implement a simple
 * non-reentrant mutual exclusion lock rather than use
 * ReentrantLock because we do not want worker tasks to be able to
 * reacquire the lock when they invoke pool control methods like
 * setCorePoolSize.  Additionally, to suppress interrupts until
 * the thread actually starts running tasks, we initialize lock
 * state to a negative value, and clear it upon start (in
 * runWorker).
 */
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable
{
    // ...

    /** Thread this worker is running in.  Null if factory fails. */
    final Thread thread;
    /** Initial task to run.  Possibly null. */
    Runnable firstTask;

    /**
     * Creates with given first task and thread from ThreadFactory.
     * @param firstTask the first task (null if none)
     */
    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);
    }

    /** Delegates main run loop to outer runWorker. */
    public void run() {
        runWorker(this);
    }

    // ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

這裏我只貼出了 Worker類中和本次分析有關的代碼, 其他代碼就省略掉了. Worker類內部還包含一個線程對象 thread 和一個 Runnable對象 firstTask. 而這個線程對象的創建過程見35行, 將Worker對象自身作爲這個線程對象 thread的 Runnable參數傳遞給 thread的構造方法. 那麼我們就可以知道, 如果要運行該線程, 也就是執行 thread.start(), 那麼實際上就是要執行 thread所在的 Worker類中的 run()方法, 而從第40行又可以知道, Worker類中的 run()方法又是調用 runWorker(this); 這個方法的, 並將 thread所在的 Worker對象作爲這個 runWorker()方法的參數. 簡單來說就是:

啓動一個 Worker對象中包含的線程 thread, 就相當於要執行 runWorker()方法, 並將該 Worker對象作爲該方法的參數.

我們對 Worker類的分析就到此爲止吧, 我們再回到 addWorker()方法繼續來分析. 我們看 addWorker() 方法的第108行, 啓動了線程 t, 而從第80行我們可以知道, 線程 t 就是 w.thread 所指向的對象, 所以第108行就相當於啓動了 w.thread 這個線程, 也就是啓動了 Worker對象 w 中的線程對象 thread. 而我們在分析 Worker類時, 曾得出過這樣的結論: 啓動一個 Worker對象中包含的線程 thread, 就相當於要執行 runWorker()方法, 並將該 Worker對象作爲該方法的參數. (結論見這裏) 所以, 這裏要啓動線程 w.thread, 也就相當於要執行 runWorker(w)方法. 我們再來看一下 runWorker()方法的源碼吧: 

/**
 * Main worker run loop.  Repeatedly gets tasks from queue and
 * executes them, while coping with a number of issues:
 *
 * 1. We may start out with an initial task, in which case we
 * don't need to get the first one. Otherwise, as long as pool is
 * running, we get tasks from getTask. If it returns null then the
 * worker exits due to changed pool state or configuration
 * parameters.  Other exits result from exception throws in
 * external code, in which case completedAbruptly holds, which
 * usually leads processWorkerExit to replace this thread.
 *
 * 2. Before running any task, the lock is acquired to prevent
 * other pool interrupts while the task is executing, and then we
 * ensure that unless pool is stopping, this thread does not have
 * its interrupt set.
 *
 * 3. Each task run is preceded by a call to beforeExecute, which
 * might throw an exception, in which case we cause thread to die
 * (breaking loop with completedAbruptly true) without processing
 * the task.
 *
 * 4. Assuming beforeExecute completes normally, we run the task,
 * gathering any of its thrown exceptions to send to afterExecute.
 * We separately handle RuntimeException, Error (both of which the
 * specs guarantee that we trap) and arbitrary Throwables.
 * Because we cannot rethrow Throwables within Runnable.run, we
 * wrap them within Errors on the way out (to the thread's
 * UncaughtExceptionHandler).  Any thrown exception also
 * conservatively causes thread to die.
 *
 * 5. After task.run completes, we call afterExecute, which may
 * also throw an exception, which will also cause thread to
 * die. According to JLS Sec 14.20, this exception is the one that
 * will be in effect even if task.run throws.
 *
 * The net effect of the exception mechanics is that afterExecute
 * and the thread's UncaughtExceptionHandler have as accurate
 * information as we can provide about any problems encountered by
 * user code.
 *
 * @param w the worker
 */
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // 由前邊可知, task 就是 w.firstTask
        // 如果 task爲 null, 那麼就不進入該 while循環, 也就不運行該 task. 如果    
        // task不爲 null, 那麼就執行 getTask()方法. 而getTask()方法是個無限
        // 循環, 會從阻塞隊列 workQueue中不斷取出任務來執行. 當阻塞隊列 workQueue
        // 中所有的任務都被取完之後, 就結束下面的while循環.
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // If pool is stopping, ensure thread is interrupted;
            // if not, ensure thread is not interrupted.  This
            // requires a recheck in second case to deal with
            // shutdownNow race while clearing interrupt
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    // 執行從阻塞隊列 workQueue中取出的任務.
                    task.run();
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                // 將 task 置爲 null, 這樣使得 while循環是否繼續執行的判斷, 就只能依賴於判斷
                // 第二個條件, 也就是 (task = getTask()) != null 這個條件, 是否滿足.
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

第46行, 將 w.firstTask 賦值給局部變量 task. 
第56行, 這一行分爲下面兩部分內容來介紹:

(1) 
如果 task 爲 null (也就是 w.firstTask 爲 null), 那麼就不進入 while循環, 也就不運行 task. 
還記得在分析 execute()方法的源碼 時, 我們提到過, 該方法會在三種情況下分別調用 addWorker()方法嗎? 只是這三次調用時, 分別傳入的參數都不同, 而該方法第98, 99行代碼以及我們對該代碼的註釋如下:

// 再次計算線程池內的有效線程數 workerCount, 一旦發現該數量變爲0, 
// 就將線程池內的線程數上限值設置爲最大線程數 maximumPoolSize, 然後
// 只是創建一個線程而不去啓動它, 並結束整個 execute()方法的執行.
else if (workerCountOf(recheck) == 0)
    addWorker(null, false);
  • 1
  • 2
  • 3
  • 4
  • 5

我們在註釋中寫到”一旦發現該數量變爲0, 就只是創建一個線程而不去啓動它”, 而在分析過 Worker類 以及 addWorker() 和 runWorker() 方法的源碼後, 這句註釋就很容易理解了. 下面我們就來分析一下 addWorker(null, false); 這句代碼的含義吧. 這句代碼爲參數 firstTask 賦值爲 null, 爲參數 core 賦值爲 false. 回到 addWorker()方法的源碼, 第78行有如下代碼:

w = new Worker(firstTask);
  • 1

我們傳遞的 firstTask 爲 null, 再看 Worker類的源碼, 第32~36行是其構造方法, 在構造方法中, 將值爲 null 的參數 firstTask 傳給該Worker類內部的 Runnable字段 firstTask (也就是設置 w.firstTask = null), 而在 addWorker()方法的第108行又去運行線程t, 也就是運行線程 w.thread, 即: 啓動 Worker 對象 w 中的線程 thread. 而我們又在前邊總結過一個結論: 啓動一個 Worker對象中包含的線程 thread, 就相當於要執行 runWorker()方法, 並將該 Worker對象作爲該方法的參數. (見這裏) 所以, 啓動線程 w.thread, 就相當於是執行 runWorker(w)方法, 而我們還需要記住, 此時的 w.firstTask 是 null 的, 所以再回到 runWorker()方法的源碼, 見第46行, 我們將值爲 null 的 w.firstTask 傳遞給局部變量 task, 這樣 task 也爲 null. 這樣第56行 while循環的 task != null 判斷條件就爲 false, 從而直接跳過該 while 循環, 也就跳過了該 while 循環中第72行 task.run(); 這句啓動線程的代碼, 所以說, addWorker(null, false); 這句代碼的含義是”只是創建一個線程而不去啓動它”.

(2) 
我們繼續回到 runWorker() 方法的第56行. 我們在 (1) 中分析了 w.firstTask 爲 null 導致直接 while()循環直接不執行的情況. 而如果 w.firstTask 不爲 null (也就是我們提交的任務不爲 null). 那麼在執行到 runWorker() 方法的第56行時, 由於滿足了第一個條件 task != null, 所以會進入到 while 循環中, 在72行會去執行我們提交的任務, 然後在第85行, 將 task 置爲 null, 使得下次循環開始時, 也就是再次來到該方法的第56行時, 由於第一個條件 task != null 爲 false, 使得我們必須去判斷第二個條件 (task = getTask()) != null 是否成立. 如果 getTask() 方法的返回值不爲 null, 那麼該方法會將其返回值賦值給 task, 然後再次進入 while 循環, 然後還是在第72行去執行這個 task, 在第85行再次將 task 置爲 null, 又回到第56行, 第一個條件 task != null 依然爲 false, 所以再次需要判斷第二個條件 (task = getTask()) != null 是否成立, 又一次執行 getTask()方法……這樣一直循環下去, 直到 getTask() 方法返回 null, 纔會結束 while循環, 然後結束掉 runWorker() 方法. 那麼, getTask() 方法到底是用來做什麼的呢? 其實, 根據該方法的名字, 想必你應該已經猜到了吧, “獲取任務”, 這個猜測對不對呢? 我們還是來看看它的源碼吧, 如下所示:

/**
 * Performs blocking or timed wait for a task, depending on
 * current configuration settings, or returns null if this worker
 * must exit because of any of:
 * 1. There are more than maximumPoolSize workers (due to
 *    a call to setMaximumPoolSize).
 * 2. The pool is stopped.
 * 3. The pool is shutdown and the queue is empty.
 * 4. This worker timed out waiting for a task, and timed-out
 *    workers are subject to termination (that is,
 *    {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
 *    both before and after the timed wait, and if the queue is
 *    non-empty, this worker is not the last thread in the pool.
 *
 * @return task, or null if the worker must exit, in which case
 *         workerCount is decremented
 */
private Runnable getTask() {
    boolean timedOut = false; // Did the last poll() time out?

    // 無限循環.
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // 如果線程池已停止, 或者線程池被關閉並且線程池內的阻塞隊列爲空, 則結束該方法並返回 null.
        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);


        // 如果 allowCoreThreadTimeOut 這個字段設置爲 true(也就是允許核心線程受超時機制的控制), 則
        // 直接設置 timed 爲 true. 反之, 則再看當前線程池中的有效線程數是否已經超過了核心線程數, 也
        // 就是是否存在非核心線程. 如果存在非核心線程, 那麼也會設置 timed 爲true. 
        // 如果 wc <= corePoolSize (線程池中的有效線程數少於核心線程數, 即: 線程池內運行着的都是核心線程), 
        // 並且 allowCoreThreadTimeOut 爲 false(即: 核心線程即使空閒, 也不會受超時機制的限制), 
        // 那麼就設置 timed 爲 false.
        // Are workers subject to culling?
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

        // 當線程池處於 RUNNING (運行)狀態但阻塞隊列內已經沒有任務(爲空)時, 將導致有線程接下來會一直
        // 處於空閒狀態. 如果空閒的是核心線程並且設置核心線程不受超時機制的影響(默認情況下就是這個設置), 
        // 那麼這些核心線程將一直在線程池中處於空閒狀態, 等待着新任務的到來, 只要線程池處於 RUNNING 
        // (運行)狀態, 那麼, 這些空閒的核心線程將一直在池子中而不會被銷燬. 如果空閒的是非核心線程, 或者
        // 雖然是核心線程但是設置了核心線程受超時機制的限制, 那麼當空閒達到超時時間時, 這就滿足了這裏的
        // if條件而去執行 if內部的代碼, 通過返回 null 結束掉該 getTask()方法, 也最終結束掉 runWorker()方法.
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }

        try {
            // 從阻塞隊列中取出隊首的那個任務, 設置給 r. 如果空閒線程等待超時或者該隊列已經爲空, 則 r爲 null.
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();

            // 如果阻塞隊列不爲空並且未發生超時的情況, 那麼取出的任務就不爲 null, 就直接返回該任務對象. 
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72

getTask()方法內有個無限循環, 要想結束一個無限循環, 要麼用 break 語句, 要麼用 return 語句. 而觀察上述源碼可以發現, 也只有第30, 54, 66行滿足結束循環的條件, 三個return語句, 我們接下來會分別進行分析.

先看第30行的 return null; 語句, 這是當線程池處於關閉或者停止時, 如果滿足其他條件, 就會結束該循環, 並返回 null, 表示線程池已關閉且阻塞隊列已空, 或者線程池已停止, 這兩種情況下都沒有或無法獲取要處理的任務了. 再看第60~62行, 嘗試從阻塞隊列中取出位於隊首的那個任務, 如果發生了線程等待超時, 或者執行取出動作之前, 阻塞隊列已經空了, 那麼任務 r 就爲 null; 如果隊列中還有任務並且未發生超時, 那麼 r 就不爲 null. 第65~66行, 如果 r 不爲 null, 就表示此次取出的確實是一個實實在在的任務對象, 於是就將該任務對象返回並結束該無限循環和該方法. 當然, 我們終究是會遇到執行取出動作的時候阻塞隊列已經爲空的情況, 那麼取出的 r 就爲 null, 這樣就不滿足第65行的條件, 也就不會執行第66行的 return 語句, 這樣又會來到第22行, 再次執行這個無限循環. 如果檢測到超時條件滿足了, 由於此時阻塞隊列爲空, 那麼這就滿足了第51~52行的條件, 就會執行第54行的 return null; 語句, 表示已經沒有可以取出的任務了, 所以返回 null, 並結束該無限循環也結束該方法.

總之, 如果有任務可以取出的話, getTask()方法就會返回一個具體的任務對象; 如果線程池被關閉或停止, 或者阻塞隊列在相當長的一段時間 (超過超時時間) 內一直沒有任務可供取出, 那麼就會返回 null, 表示沒有任務了或者無法再獲取任務了, 那麼 runWorker() 方法 第56行的判斷條件 (task = getTask()) != null 就爲 false, 導致程序退出 while 循環, 最終結束該方法.

以上就是線程池對任務調度的大致原理. 至此, 這篇文章也就結束了. 感謝各位有耐心的讀者能夠讀完這篇相對較長也較爲枯燥的文章, 也希望大家能夠多提寶貴的建議或意見, 互相交流學習!



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章