ThreadPoolExecutor源碼分析(一):重要的成員變量

ThreadPoolExecutor部分重要成員變量:
1、AtomicInteger ctl
2、workQueue
3、corePoolSize
4、maximumPoolSize
5、keepAliveTime
6、handler

一、AtomicInteger ctl

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    // 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;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

AtomicInteger類型的ctl代表了ThreadPoolExecutor中的控制狀態,它是一個複覈類型的成員變量,是一個原子整數,藉助高低位包裝了兩個概念:

  1. workerCount:線程池中當前活動的線程數量,佔據ctl的低29位;
  2. runState:線程池運行狀態,佔據ctl的高3位,有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED五種狀態。

workerCount:

COUNT_BITS的定義代表了workerCount所佔位數:

private static final int COUNT_BITS = Integer.SIZE - 3;
//在Java中,一個int佔據32位,而COUNT_BITS的結果不言而喻,Integer大小32減去3,就是29。

CAPACITY的定義限制了workerCount的理論最大活躍線程數:

private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
//運算過程爲1左移29位,也就是00000000 00000000 00000000 00000001 --> 001 0000 00000000 00000000 00000000,
//再減去1,就是 000 11111 11111111 11111111 11111111,
//前三位代表線程池運行狀態runState,
//所以這裏workerCount的理論最大值就應該是29個1,即536870911。

workerCount作爲其中一個概念複合在AtomicInteger ctl中,ThreadPoolExecutor也提供了從AtomicInteger ctl中解析出workerCount的方法,如下:

private static int workerCountOf(int c)  { 
    return c & CAPACITY; 
}
//傳入的c代表的是ctl的值,即高3位爲線程池運行狀態runState,低29位爲線程池中當前活動的線程數量workerCount.
//將其與CAPACITY進行與操作&,也就是與000 11111 11111111 11111111 11111111進行與操作,
//c的前三位通過與000進行與操作,無論c前三位爲何值,最終都會變成000,也就是捨棄前三位的值,
//而c的低29位與29個1進行與操作,c的低29位還是會保持原值,
//這樣就從AtomicInteger ctl中解析出了workerCount的值。

runState:

線程池運行狀態,它佔據ctl的高3位,有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED五種狀態。我們先分別解釋下這五種狀態:

  1. RUNNING:接受新任務,並處理隊列任務
//Accept new tasks and process queued tasks
private static final int RUNNING    = -1 << COUNT_BITS;

-1在Java底層是由32個1表示的,左移29位的話,即111 00000 00000000 00000000 00000000,也就是低29位全部爲0,高3位全部爲1的話,表示RUNNING狀態,即-536870912;

  1. SHUTDOWN:不接受新任務,但會處理隊列任務
//Don't accept new tasks, but process queued tasks
private static final int SHUTDOWN   =  0 << COUNT_BITS;

0在Java底層是由32個0表示的,無論左移多少位,還是32個0,即000 00000 00000000 00000000 00000000,也就是低29位全部爲0,高3位全部爲0的話,表示SHUTDOWN狀態,即0。

  1. STOP:不接受新任務,不會處理隊列任務,而且會中斷正在處理過程中的任務
//Don't accept new tasks, don't process queued tasks,
//and interrupt in-progress tasks
private static final int STOP       =  1 << COUNT_BITS;

1在Java底層是由前面的31個0和1個1組成的,左移29位的話,即001 00000 00000000 00000000 00000000,也就是低29位全部爲0,高3位爲001的話,表示STOP狀態,即536870912;

  1. TIDYING:所有的任務已結束,workerCount爲0,線程過渡到TIDYING狀態,將會執行terminated()鉤子方法。
/*
All tasks have terminated, workerCount 
is zero,the thread transitioning to 
state TIDYING will run the terminated() hook method
*/
private static final int TIDYING    =  2 << COUNT_BITS;

2在Java底層是由前面的30個0和1個10組成的,左移29位的話,即010 00000 00000000 00000000 00000000,也就是低29位全部爲0,高3位爲010的話,表示TIDYING狀態,即1073741824。

  1. TERMINATED:terminated()方法已經完成
//terminated() has completed
private static final int TERMINATED =  3 << COUNT_BITS;

3在Java底層是由前面的30個0和1個11組成的,左移29位的話,即011 00000 00000000 00000000 00000000,也就是低29位全部爲0,高3位爲011的話,表示TERMINATED狀態,即1610612736。

由上面我們可以得知,運行狀態的值按照RUNNING-->SHUTDOWN-->STOP-->TIDYING-->TERMINATED順序值是遞增的,這些值之間的數值順序很重要。隨着時間的推移,運行狀態單調增加,但是不需要經過每個狀態。那麼,可能存在的線程池狀態的轉換是什麼呢?如下:

     * 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
     *
  1. RUNNING -> SHUTDOWN:調用shutdown()方法後,或者線程池實現了finalize方法,在裏面調用了shutdown方法,即隱式調用。
  2. (RUNNING or SHUTDOWN) -> STOP:調用shutdownNow()方法後。
  3. SHUTDOWN -> TIDYING:線程池和隊列均爲空時。
  4. STOP -> TIDYING:線程池爲空時。
  5. TIDYING -> TERMINATED:terminated()鉤子方法完成時。

接下來看看如何從ctl中提取runState:

private static int runStateOf(int c)     { return c & ~CAPACITY; }

~ 是按位取反的意思,CAPACITY表示的是高位的3個0,和低位的29個1,而~CAPACITY則表示高位的3個1,2低位的9個0,然後再與入參c執行按位與操作,即高3位保持原樣,低29位全部設置爲0,也就獲取了線程池的運行狀態runState。

最後看一下這個方法:

private static int ctlOf(int rs, int wc) { return rs | wc; }

將runState和workerCount做或操作|處理,即用runState的高3位,workerCount的低29位填充的數字,而默認傳入的runState、workerCount分別爲RUNNING和0。

接下來看一下其他幾個重要的成員變量:

    /**
     * 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;

    /**
     * Lock held on access to workers set and related bookkeeping.
     * While we could use a concurrent set of some sort, it turns out
     * to be generally preferable to use a lock. Among the reasons is
     * that this serializes interruptIdleWorkers, which avoids
     * unnecessary interrupt storms, especially during shutdown.
     * Otherwise exiting threads would concurrently interrupt those
     * that have not yet interrupted. It also simplifies some of the
     * associated statistics bookkeeping of largestPoolSize etc. We
     * also hold mainLock on shutdown and shutdownNow, for the sake of
     * ensuring workers set is stable while separately checking
     * permission to interrupt and actually interrupting.
     */
    private final ReentrantLock mainLock = new ReentrantLock();

    /**
     * Set containing all worker threads in pool. Accessed only when
     * holding mainLock.
     */
    private final HashSet<Worker> workers = new HashSet<Worker>();

    /**
     * Wait condition to support awaitTermination
     */
    private final Condition termination = mainLock.newCondition();

    /**
     * Tracks largest attained pool size. Accessed only under
     * mainLock.
     */
    private int largestPoolSize;

    /**
     * Counter for completed tasks. Updated only on termination of
     * worker threads. Accessed only under mainLock.
     */
    private long completedTaskCount;

    /*
     * All user control parameters are declared as volatiles so that
     * ongoing actions are based on freshest values, but without need
     * for locking, since no internal invariants depend on them
     * changing synchronously with respect to other actions.
     */

    /**
     * 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;

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

    /**
     * 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;

    /**
     * If false (default), core threads stay alive even when idle.
     * If true, core threads use keepAliveTime to time out waiting
     * for work.
     */
    private volatile boolean allowCoreThreadTimeOut;

    /**
     * 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;

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

    /**
     * The default rejected execution handler
     */
    private static final RejectedExecutionHandler defaultHandler =
        new AbortPolicy();

private final BlockingQueue<Runnable> workQueue;              //任務緩存隊列,用來存放等待執行的任務
private final ReentrantLock mainLock = new ReentrantLock();   //線程池的主要狀態鎖,對線程池狀態(比如線程池大小
                                                              //、runState等)的改變都要使用這個鎖
private final HashSet<Worker> workers = new HashSet<Worker>();  //用來存放工作集
 
private volatile long  keepAliveTime;    //線程存貨時間   
private volatile boolean allowCoreThreadTimeOut;   //是否允許爲核心線程設置存活時間
private volatile int   corePoolSize;     //核心池的大小(即線程池中的線程數目大於這個參數時,提交的任務會被放進任務緩存隊列)
private volatile int   maximumPoolSize;   //線程池最大能容忍的線程數
 
private volatile int   poolSize;       //線程池中當前的線程數
 
private volatile RejectedExecutionHandler handler; //任務拒絕策略
 
private volatile ThreadFactory threadFactory;   //線程工廠,用來創建線程
 
private int largestPoolSize;   //用來記錄線程池中曾經出現過的最大線程數
 
private long completedTaskCount;   //用來記錄已經執行完畢的任務個數

二、workQueue:

阻塞隊列,用來存儲等待執行的任務,這裏的阻塞隊列有以下幾種選擇:

ArrayBlockingQueue:基於數組的先進先出隊列,此隊列創建時必須指定大小;
LinkedBlockingQueue:基於鏈表的先進先出隊列,如果創建時沒有指定此隊列大小,則默認爲Integer.MAX_VALUE;
synchronousQueue:這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個線程來執行新來的任務。

三、corePoolSize:

核心池的大小,這個參數跟後面講述的線程池的實現原理有非常大的關係。在創建了線程池後,默認情況下,線程池中並沒有任何線程,而是等待有任務到來才創建線程去執行任務,除非調用了prestartAllCoreThreads()或者prestartCoreThread()方法,從這2個方法的名字就可以看出,是預創建線程的意思,即在沒有任務到來之前就創建corePoolSize個線程或者一個線程。默認情況下,在創建了線程池後,線程池中的線程數爲0,當有任務來之後,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize後,就會把到達的任務放到緩存隊列當中。

四、maximumPoolSize:

線程池最大線程數,這個參數也是一個非常重要的參數,它表示在線程池中最多能創建多少個線程;

五、keepAliveTime:

表示線程沒有任務執行時最多保持多久時間會終止。默認情況下,只有當線程池中的線程數大於corePoolSize時,keepAliveTime纔會起作用,直到線程池中的線程數不大於corePoolSize,即當線程池中的線程數大於corePoolSize時,如果一個線程空閒的時間達到keepAliveTime,則會終止,直到線程池中的線程數不超過corePoolSize。但是如果調用了allowCoreThreadTimeOut(boolean)方法,在線程池中的線程數不大於corePoolSize時,keepAliveTime參數也會起作用,直到線程池中的線程數爲0;

六、handler:

表示當拒絕處理任務時的策略,有以下四種取值:

//當線程池的任務緩存隊列已滿並且線程池中的線程數目達到maximumPoolSize,如果還有任務到來就會採取任務拒絕策略
ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。 
ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。 
ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章