執行緒池在多執行緒程式設計的中可謂是個利器,使用執行緒池會大大提高多執行緒的效率。原因是使用執行緒池相對於new Thread
有效避免了執行緒建立和銷燬的開銷。
在Java
中一般來說通過Executors
來建立所需要的執行緒池,如:Executors.newFixedThreadPool
、Executors.newScheduledThreadPool
等等。而其中一種主要的實現就是ThreadPoolExecutor
。ThreadPoolExecutor
是J.U.C
包下的,當然也是出自大神Doug Lea
之手。
ThreadPoolExecutor 結構
如上圖所示,ThreadPoolExecutor
類大致結構如上圖所示。Executor
介面只有一個execute
方法。ExecutorSerivce
介面在Executor
的基礎上提供了對任務執行的管理,如:shutdown
方法。AbstractExecutorService
是針對其中invokeXXX
和submit
做了預設的實現。真正的execute
將由ThreadPoolExecutor
自身實現。
ThreadPoolExecutor
還有幾個內部類:AbortPolicy
、DiscardPolicy
、DiscardOldestPolicy
、CallerRunsPolicy
。這幾個類的作用是當執行緒池承載很多工之後,超過maximumPoolSize
的數量,無法繼續接受任務的時候,提供了不同的拒絕策略。
Worker
類也是ThreadPoolExecutor
的內部類,實現Runnable
介面,提供了工作執行緒的實現。後面會詳細分析這個類的實現。
ThreadPoolExecutor
類提供了4
個構造方法。其中最核心的構造方法如下所示:
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;
}複製程式碼
引數含義如下:
引數 | 含義 |
---|---|
corePoolSize | 核心執行緒池大小 |
maximumPoolSize | 最大執行緒池大小 |
keepAliveTime | 超過corePoolSize數量的空閒執行緒最大存活時間 |
corePoolSize | keepAliveTime 時間單位 |
workQueue | 工作佇列 |
ThreadFactory | 執行緒工廠 |
RejectedExecutionHandler | 拒絕策略,預設拒絕策略:AbortPolicy |
ThreadPoolExecutor 類變數
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
ctl
是一個AtomicInteger
修飾的變數,用來儲存2個變數:
- workerCount: 執行緒池中活動執行緒的數量
- runState:執行緒池的執行狀態
workerCount
workerCount
佔用低29位存執行緒數,workerCount
代表了執行緒池活動的執行緒數量,最小數量是0,最大的數量是(1 << COUNT_BITS) - 1
(536870911);
1 << COUNT_BITS :
00000000 00000000 00000000 00000001 --> 00100000 00000000 00000000 00000000
(1 << COUNT_BITS) - 1 :
00100000 00000000 00000000 00000000 --> 00011111 11111111 11111111 11111111複製程式碼
private static int workerCountOf(int c) { return c & CAPACITY; }複製程式碼
workerCountOf
方法是用從ctl
中解析出workerCount
的值來。由於CAPACITY
高3位是000
,ctl
的值與CAPACITY
做&
操作的時候,高3位將被捨棄;由於CAPACITY
低29位是全是1
,&
操作會保持原值,這樣workerCount
的值就從ctl
中解析出來了。
runState
runState
佔用高3位存執行緒狀態,共有5個值:
RUNNING(-536870912)
:接受新任務,並處理佇列任務SHUTDOWN(0)
: 不接受新任務,但會處理佇列中的任務STOP(536870912)
: 不接受新任務,不會處理佇列任務,中斷正在處理的任務TIDYING(1073741824)
: 所有任務已結束,workerCount
為0,執行緒過渡到TIDYING
狀態,將呼叫terminated()
方法TERMINATED(1610612736)
: terminated()方法已經完成
private static int runStateOf(int c) { return c & ~CAPACITY; }複製程式碼
runStateOf
方法的作用是解析出runState
的值。~CAPACITY
將反轉CAPACITY
的值,也就是CAPACITY
的高3位全部為1,低29位全部為0。如此ctl & ~CAPACITY
就會送ctl
中解析出runState
的值。
worker 類
執行緒池維護的是HashSet<Worker> workers
,一個由Worker
物件組成的HashSet
。
// 繼承AQS,實現Runnable介面
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
// 處理任務的執行緒
final Thread thread;
// worker 傳入的任務
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* 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);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}複製程式碼
Worker
繼承與AQS
,主要想利用AQS
獨佔所機制,來標示執行緒是否空閒。後續的shutdown
方法在執行時候,判斷執行緒是否空閒,tryLock
來實現的。同樣可以看到在Worker
在初始化的時候將state
設定為-1
,這也是為了避免worker
在執行前被中斷。
Worker
繼承與Runnable
,當worker
啟動時候就會呼叫run
方法。Worker
類的run
方法呼叫了runWorker
方法;
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// 將state設定為0,允許中斷
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// task為空,則呼叫getTask(),從workQueue取出新的task
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
// 執行緒池如果是STOP狀態,要中斷當前執行緒
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
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,迴圈操作
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}複製程式碼
execute 方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}複製程式碼
execute
主要分3個步驟:
- 當活動執行緒小於
corePoolSize
的值,將會呼叫addWorker
建立新的任務執行緒; - 如果任務可以成功被加入至
workQueue
,這是將再次獲取ctl
的值做雙重校驗,如果執行緒池已經shutdown
,將從workQueue
移除並拒絕該任務。 - 如果任務佇列加入失敗,則拒絕任務。
addWorker 方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 執行緒池狀態大於SHUTDOWN值將不再接受新的任務,建立執行緒。
// rs == SHUTDOWN or firstTask == null or workQueue.isEmpty() 都不再接受任務
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
// 可用執行緒數不足,分配任務失敗
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// CAS操作增加workerCount的值,增加成功跳出迴圈
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
}
}
// workerCount增加成功進入下面的邏輯
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
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.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
// 任務執行緒啟動失敗呼叫addWorkerFailed,將執行緒從執行緒池移除,將workerCount -1
addWorkerFailed(w);
}
return workerStarted;
}複製程式碼
shutdown 方法
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 設定執行緒池狀態為:SHUTDOWN
advanceRunState(SHUTDOWN);
// 中斷所有任務執行緒
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}複製程式碼
呼叫執行緒池shutdown
方法,將不再接受新的任務。首先會呼叫checkShutdownAccess
方法檢驗是否具有執行緒shutdown
的許可權,然後將執行緒池的狀態設定為SHUTDOWN
,最後中斷空閒執行緒,這裡的中斷是用Thread.interrupt()
實現的,所以不會影響正在執行的執行緒,正在執行的的任務將會繼續執行。
shutdownNow 方法
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 將執行緒池狀態設定為:STOP
advanceRunState(STOP);
// 中斷所有執行緒
interruptWorkers();
// 獲取佇列中尚未被執行的任務
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}複製程式碼
shutdownNow方法與shutdown方法類似,不同是shutdownNow
會將執行緒池的狀態設定STOP
,在中斷過程中,少了tryLock
獲取鎖的操作,所以不管執行緒是否空閒都將被中斷,中斷所有執行緒,但也不會強行終止正在執行的執行緒。最後返回阻塞佇列中沒有被執行的任務list
。