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中的控制狀態,它是一個複覈類型的成員變量,是一個原子整數,藉助高低位包裝了兩個概念:
- workerCount:線程池中當前活動的線程數量,佔據ctl的低29位;
- 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五種狀態。我們先分別解釋下這五種狀態:
- 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;
- 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。
- 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;
- 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。
- 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
*
- RUNNING -> SHUTDOWN:調用shutdown()方法後,或者線程池實現了finalize方法,在裏面調用了shutdown方法,即隱式調用。
- (RUNNING or SHUTDOWN) -> STOP:調用shutdownNow()方法後。
- SHUTDOWN -> TIDYING:線程池和隊列均爲空時。
- STOP -> TIDYING:線程池爲空時。
- 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:由調用線程處理該任務