一、前言
JUC這部分還有執行緒池這一塊沒有分析,需要抓緊時間分析,下面開始ThreadPoolExecutor,其是執行緒池的基礎,分析完了這個類會簡化之後的分析,執行緒池可以解決兩個不同問題:由於減少了每個任務呼叫的開銷,它們通常可以在執行大量非同步任務時提供增強的效能,並且還可以提供繫結和管理資源(包括執行任務集時使用的執行緒)的方法。下面開始分析。
二、ThreadPoolExecutor資料結構
在ThreadPoolExecutor的內部,主要由BlockingQueue和AbstractQueuedSynchronizer對其提供支援,BlockingQueue介面有多種資料結構的實現,如LinkedBlockingQueue、ArrayBlockingQueue等,而AbstractQueuedSynchronizer在之前有過詳細的分析,有興趣的讀者可以參考。
三、ThreadPoolExecutor原始碼分析
3.1 類的繼承關係
public class ThreadPoolExecutor extends AbstractExecutorService {}
說明:ThreadPoolExecutor繼承自AbstractExecutorService,AbstractExecuetorService提供了ExecutorService執行方法的預設實現。
3.2 類的內部類
ThreadPoolExecutor的核心內部類為Worker,其對資源進行了複用,減少建立執行緒的開銷,還有若干個策略類。內部類的類圖如下
說明:可以看到Worker繼承了AQS抽象類並且實現了Runnable介面,其是ThreadPoolExecutor的核心內部類。而對於AbortPolicy,用於被拒絕任務的處理程式,它將丟擲 RejectedExecutionException、CallerRunsPolicy,用於被拒絕任務的處理程式,它直接在 execute 方法的呼叫執行緒中執行被拒絕的任務;如果執行程式已關閉,則會丟棄該任務、DiscardPolicy,用於被拒絕任務的處理程式,預設情況下它將丟棄被拒絕的任務、DiscardOldestPolicy,用於被拒絕任務的處理程式,它放棄最舊的未處理請求,然後重試 execute;如果執行程式已關閉,則會丟棄該任務。這些都是拒絕任務提交時的所採用的不同策略。
① Worker類
1. 類的繼承關係
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {}
說明:Worker繼承了AQS抽象類,其重寫了AQS的一些方法,並且其也可作為一個Runnable物件,從而可以建立執行緒Thread。
2. 類的屬性
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; /** Thread this worker is running in. Null if factory fails. */ // worker 所對應的執行緒 final Thread thread; /** Initial task to run. Possibly null. */ // worker所對應的第一個任務 Runnable firstTask; /** Per-thread task counter */ // 已完成任務數量 volatile long completedTasks; }
說明:Worker屬性中比較重要的屬性如下,Thread型別的thread屬性,用來封裝worker(因為worker為Runnable物件),表示一個執行緒;Runnable型別的firstTask,其表示該worker所包含的Runnable物件,即使用者自定義的Runnable物件,完成使用者自定義的邏輯的Runnable物件;volatile修飾的long型別的completedTasks,表示已完成的任務數量。
3. 類的建構函式
Worker(Runnable firstTask) { // 設定狀態為-1 setState(-1); // inhibit interrupts until runWorker // 初始化第一個任務 this.firstTask = firstTask; // 根據當前worker,初始化執行緒 this.thread = getThreadFactory().newThread(this); }
說明:用於構造一個worker物件,並設定AQS的state為-1,同時初始化了對應的域。
4. 核心函式分析
// 重寫了Runnable的run方法 public void run() { runWorker(this); } // Lock methods // // The value 0 represents the unlocked state. // The value 1 represents the locked state. // 是否被獨佔,0代表未被獨佔,1代表被獨佔 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) { // 設定獨佔執行緒為null setExclusiveOwnerThread(null); // 設定狀態為0 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()) { // AQS狀態大於等於0並且worker對應的執行緒不為null並且該執行緒沒有被中斷 try { // 中斷執行緒 t.interrupt(); } catch (SecurityException ignore) { } } }
說明:Worker的函式主要是重寫了AQS的相應函式和重寫了Runnable的run函式,重寫的函式比較簡單,具體的可以參見AQS的分析,這裡不再累贅。
3.3 類的屬性
public class ThreadPoolExecutor extends AbstractExecutorService { // 執行緒池的控制狀態(用來表示執行緒池的執行狀態(整形的高3位)和執行的worker數量(低29位)) private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); // 29位的偏移量 private static final int COUNT_BITS = Integer.SIZE - 3; // 最大容量(2^29 - 1) private static final int CAPACITY = (1 << COUNT_BITS) - 1; // runState is stored in the high-order bits // 執行緒執行狀態,總共有5個狀態,需要3位來表示(所以偏移量的29 = 32 - 3) 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; // 阻塞佇列 private final BlockingQueue<Runnable> workQueue; // 可重入鎖 private final ReentrantLock mainLock = new ReentrantLock(); // 存放工作執行緒集合 private final HashSet<Worker> workers = new HashSet<Worker>(); // 終止條件 private final Condition termination = mainLock.newCondition(); // 最大執行緒池容量 private int largestPoolSize; // 已完成任務數量 private long completedTaskCount; // 執行緒工廠 private volatile ThreadFactory threadFactory; // 拒絕執行處理器 private volatile RejectedExecutionHandler handler; // 執行緒等待執行時間 private volatile long keepAliveTime; // 是否執行核心執行緒超時 private volatile boolean allowCoreThreadTimeOut; // 核心池的大小 private volatile int corePoolSize; // 最大執行緒池大小 private volatile int maximumPoolSize; // 預設拒絕執行處理器 private static final RejectedExecutionHandler defaultHandler = new AbortPolicy(); // private static final RuntimePermission shutdownPerm = new RuntimePermission("modifyThread"); }
說明:這裡著重講解一下AtomicInteger型別的ctl屬性,ctl為執行緒池的控制狀態,用來表示執行緒池的執行狀態(整形的高3位)和執行的worker數量(低29位)),其中,執行緒池的執行狀態有如下幾種
/** * RUNNING : 接受新任務並且處理已經進入阻塞佇列的任務 * SHUTDOWN : 不接受新任務,但是處理已經進入阻塞佇列的任務 * STOP : 不接受新任務,不處理已經進入阻塞佇列的任務並且中斷正在執行的任務 * TIDYING : 所有的任務都已經終止,workerCount為0, 執行緒轉化為TIDYING狀態並且呼叫terminated鉤子函式 * TERMINATED: terminated鉤子函式已經執行完成 **/ 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;
說明:由於有5種狀態,最少需要3位表示,所以採用的AtomicInteger的高3位來表示,低29位用來表示worker的數量,即最多表示2^29 - 1。
3.4 類的建構函式
1. ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>)型建構函式
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
說明:該建構函式用給定的初始引數和預設的執行緒工廠及被拒絕的執行處理程式建立新的 ThreadPoolExecutor。
2. ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, ThreadFactory)型建構函式
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }
說明:該建構函式用給定的初始引數和預設被拒絕的執行處理程式建立新的 ThreadPoolExecutor
3. ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, RejectedExecutionHandler)型建構函式
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler); }
說明:該建構函式用給定的初始引數和預設的執行緒工廠建立新的 ThreadPoolExecutor
4. ThreadPoolExecutor(int, int, long, TimeUnit, BlockingQueue<Runnable>, ThreadFactory, RejectedExecutionHandler)型建構函式
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) { if (corePoolSize < 0 || // 核心大小不能小於0 maximumPoolSize <= 0 || // 執行緒池的初始最大容量不能小於0 maximumPoolSize < corePoolSize || // 初始最大容量不能小於核心大小 keepAliveTime < 0) // 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; }
說明:該建構函式用給定的初始引數建立新的 ThreadPoolExecutor,其他的建構函式都會呼叫到此建構函式。
3.5 核心函式分析
1. execute函式
public void execute(Runnable command) { if (command == null) // 命令為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. */ /* * 進行下面三步 * * 1. 如果執行的執行緒小於corePoolSize,則嘗試使用使用者定義的Runnalbe物件建立一個新的執行緒 * 呼叫addWorker函式會原子性的檢查runState和workCount,通過返回false來防止在不應 * 該新增執行緒時新增了執行緒 * 2. 如果一個任務能夠成功入佇列,在新增一個線城時仍需要進行雙重檢查(因為在前一次檢查後 * 該執行緒死亡了),或者當進入到此方法時,執行緒池已經shutdown了,所以需要再次檢查狀態, * 若有必要,當停止時還需要回滾入佇列操作,或者當執行緒池沒有執行緒時需要建立一個新執行緒 * 3. 如果無法入佇列,那麼需要增加一個新執行緒,如果此操作失敗,那麼就意味著執行緒池已經shut * down或者已經飽和了,所以拒絕任務 */ // 獲取執行緒池控制狀態 int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { // worker數量小於corePoolSize if (addWorker(command, true)) // 新增worker // 成功則返回 return; // 不成功則再次獲取執行緒池控制狀態 c = ctl.get(); } if (isRunning(c) && workQueue.offer(command)) { // 執行緒池處於RUNNING狀態,將命令(使用者自定義的Runnable物件)新增進workQueue佇列 // 再次檢查,獲取執行緒池控制狀態 int recheck = ctl.get(); if (! isRunning(recheck) && remove(command)) // 執行緒池不處於RUNNING狀態,將命令從workQueue佇列中移除 // 拒絕執行命令 reject(command); else if (workerCountOf(recheck) == 0) // worker數量等於0 // 新增worker addWorker(null, false); } else if (!addWorker(command, false)) // 新增worker失敗 // 拒絕執行命令 reject(command); }
說明:當在客戶端呼叫submit時,之後會間接呼叫到execute函式,其在將來某個時間執行給定任務,此方法中並不會直接執行給定的任務。此方法中主要會呼叫到addWorker函式,其中,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. if (rs >= SHUTDOWN && // 狀態大於等於SHUTDOWN,初始的ctl為RUNNING,小於SHUTDOWN ! (rs == SHUTDOWN && // 狀態為SHUTDOWN firstTask == null && // 第一個任務為null ! workQueue.isEmpty())) // worker佇列不為空 // 返回 return false; for (;;) { // worker數量 int wc = workerCountOf(c); if (wc >= CAPACITY || // worker數量大於等於最大容量 wc >= (core ? corePoolSize : maximumPoolSize)) // worker數量大於等於核心執行緒池大小或者最大執行緒池大小 return false; if (compareAndIncrementWorkerCount(c)) // 比較並增加worker的數量 // 跳出外層迴圈 break retry; // 獲取執行緒池控制狀態 c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) // 此次的狀態與上次獲取的狀態不相同 // 跳過剩餘部分,繼續迴圈 continue retry; // else CAS failed due to workerCount change; retry inner loop } } // worker開始標識 boolean workerStarted = false; // worker被新增標識 boolean workerAdded = false; // Worker w = null; try { // 初始化worker w = new Worker(firstTask); // 獲取worker對應的執行緒 final Thread t = w.thread; if (t != null) { // 執行緒不為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 || // 小於SHUTDOWN (rs == SHUTDOWN && firstTask == null)) { // 等於SHUTDOWN並且firstTask為null if (t.isAlive()) // precheck that t is startable // 執行緒剛新增進來,還未啟動就存活 // 丟擲執行緒狀態異常 throw new IllegalThreadStateException(); // 將worker新增到worker集合 workers.add(w); // 獲取worker集合的大小 int s = workers.size(); if (s > largestPoolSize) // 佇列大小大於largestPoolSize // 重新設定largestPoolSize largestPoolSize = s; // 設定worker已被新增標識 workerAdded = true; } } finally { // 釋放鎖 mainLock.unlock(); } if (workerAdded) { // worker被新增 // 開始執行worker的run方法 t.start(); // 設定worker已開始標識 workerStarted = true; } } } finally { if (! workerStarted) // worker沒有開始 // 新增worker失敗 addWorkerFailed(w); } return workerStarted; }
說明:此函式可能會完成如下幾件任務
① 原子性的增加workerCount。
② 將使用者給定的任務封裝成為一個worker,並將此worker新增進workers集合中。
③ 啟動worker對應的執行緒,並啟動該執行緒,執行worker的run方法。
④ 回滾worker的建立動作,即將worker從workers集合中刪除,並原子性的減少workerCount。
2. runWorker函式
final void runWorker(Worker w) { // 獲取當前執行緒 Thread wt = Thread.currentThread(); // 獲取w的firstTask Runnable task = w.firstTask; // 設定w的firstTask為null w.firstTask = null; // 釋放鎖(設定state為0,允許中斷) w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { // 任務不為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) || // 執行緒池的執行狀態至少應該高於STOP (Thread.interrupted() && // 執行緒被中斷 runStateAtLeast(ctl.get(), STOP))) && // 再次檢查,執行緒池的執行狀態至少應該高於STOP !wt.isInterrupted()) // wt執行緒(當前執行緒)沒有被中斷 wt.interrupt(); // 中斷wt執行緒(當前執行緒) 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; // 增加給worker完成的任務數量 w.completedTasks++; // 釋放鎖 w.unlock(); } } completedAbruptly = false; } finally { // 處理完成後,呼叫鉤子函式 processWorkerExit(w, completedAbruptly); } }
說明:此函式中會實際執行給定任務(即呼叫使用者重寫的run方法),並且當給定任務完成後,會繼續從阻塞佇列中取任務,直到阻塞佇列為空(即任務全部完成)。在執行給定任務時,會呼叫鉤子函式,利用鉤子函式可以完成使用者自定義的一些邏輯。在runWorker中會呼叫到getTask函式和processWorkerExit鉤子函式,其中,getTask函式原始碼如下
private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { // 無限迴圈,確保操作成功 // 獲取執行緒池控制狀態 int c = ctl.get(); // 執行的狀態 int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { // 大於等於SHUTDOWN(表示呼叫了shutDown)並且(大於等於STOP(呼叫了shutDownNow)或者worker阻塞佇列為空) // 減少worker的數量 decrementWorkerCount(); // 返回null,不執行任務 return null; } // 獲取worker數量 int wc = workerCountOf(c); // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; // 是否允許coreThread超時或者workerCount大於核心大小 if ((wc > maximumPoolSize || (timed && timedOut)) // worker數量大於maximumPoolSize && (wc > 1 || workQueue.isEmpty())) { // workerCount大於1或者worker阻塞佇列為空(在阻塞佇列不為空時,需要保證至少有一個wc) if (compareAndDecrementWorkerCount(c)) // 比較並減少workerCount // 返回null,不執行任務,該worker會退出 return null; // 跳過剩餘部分,繼續迴圈 continue; } try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : // 等待指定時間 workQueue.take(); // 一直等待,直到有元素 if (r != null) return r; // 等待指定時間後,沒有獲取元素,則超時 timedOut = true; } catch (InterruptedException retry) { // 丟擲了被中斷異常,重試,沒有超時 timedOut = false; } } }
說明:此函式用於從workerQueue阻塞佇列中獲取Runnable物件,由於是阻塞佇列,所以支援有限時間等待(poll)和無限時間等待(take)。在該函式中還會響應shutDown和、shutDownNow函式的操作,若檢測到執行緒池處於SHUTDOWN或STOP狀態,則會返回null,而不再返回阻塞佇列中的Runnalbe物件。
processWorkerExit函式是在worker退出時呼叫到的鉤子函式,而引起worker退出的主要因素如下
① 阻塞佇列已經為空,即沒有任務可以執行了。
② 呼叫了shutDown或shutDownNow函式
processWorkerExit的原始碼如下
private void processWorkerExit(Worker w, boolean completedAbruptly) { if (completedAbruptly) // 如果被中斷,則需要減少workCount // If abrupt, then workerCount wasn't adjusted decrementWorkerCount(); // 獲取可重入鎖 final ReentrantLock mainLock = this.mainLock; // 獲取鎖 mainLock.lock(); try { // 將worker完成的任務新增到總的完成任務中 completedTaskCount += w.completedTasks; // 從workers集合中移除該worker workers.remove(w); } finally { // 釋放鎖 mainLock.unlock(); } // 嘗試終止 tryTerminate(); // 獲取執行緒池控制狀態 int c = ctl.get(); if (runStateLessThan(c, STOP)) { // 小於STOP的執行狀態 if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && ! workQueue.isEmpty()) // 允許核心超時並且workQueue阻塞佇列不為空 min = 1; if (workerCountOf(c) >= min) // workerCount大於等於min // 直接返回 return; // replacement not needed } // 新增worker addWorker(null, false); } }
說明:此函式會根據是否中斷了空閒執行緒來確定是否減少workerCount的值,並且將worker從workers集合中移除並且會嘗試終止執行緒池。
3. shutdown函式
public void shutdown() { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // 檢查shutdown許可權 checkShutdownAccess(); // 設定執行緒池控制狀態為SHUTDOWN advanceRunState(SHUTDOWN); // 中斷空閒worker interruptIdleWorkers(); // 呼叫shutdown鉤子函式 onShutdown(); // hook for ScheduledThreadPoolExecutor } finally { mainLock.unlock(); } // 嘗試終止 tryTerminate(); }
說明:此函式會按過去執行已提交任務的順序發起一個有序的關閉,但是不接受新任務。首先會檢查是否具有shutdown的許可權,然後設定執行緒池的控制狀態為SHUTDOWN,之後中斷空閒的worker,最後嘗試終止執行緒池。
嘗試終止執行緒池tryTerminate的原始碼如下
final void tryTerminate() { for (;;) { // 無限迴圈,確保操作成功 // 獲取執行緒池控制狀態 int c = ctl.get(); if (isRunning(c) || // 執行緒池的執行狀態為RUNNING runStateAtLeast(c, TIDYING) || // 執行緒池的執行狀態最小要大於TIDYING (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) // 執行緒池的執行狀態為SHUTDOWN並且workQueue佇列不為null // 不能終止,直接返回 return; if (workerCountOf(c) != 0) { // 執行緒池正在執行的worker數量不為0 // Eligible to terminate // 僅僅中斷一個空閒的worker interruptIdleWorkers(ONLY_ONE); return; } // 獲取執行緒池的鎖 final ReentrantLock mainLock = this.mainLock; // 獲取鎖 mainLock.lock(); try { if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { // 比較並設定執行緒池控制狀態為TIDYING try { // 終止,鉤子函式 terminated(); } finally { // 設定執行緒池控制狀態為TERMINATED ctl.set(ctlOf(TERMINATED, 0)); // 釋放在termination條件上等待的所有執行緒 termination.signalAll(); } return; } } finally { // 釋放鎖 mainLock.unlock(); } // else retry on failed CAS } }
說明:如果執行緒池的狀態為SHUTDOWN並且執行緒池和阻塞佇列都為空或者狀態為STOP並且執行緒池為空,則將執行緒池控制狀態轉化為TERMINATED;否則,將中斷一個空閒的worker,其中,interruptIdleWorkers的原始碼如下
private void interruptIdleWorkers(boolean onlyOne) { // 執行緒池的鎖 final ReentrantLock mainLock = this.mainLock; // 獲取鎖 mainLock.lock(); try { for (Worker w : workers) { // 遍歷workers佇列 // worker對應的執行緒 Thread t = w.thread; if (!t.isInterrupted() && w.tryLock()) { // 執行緒未被中斷並且成功獲得鎖 try { // 中斷執行緒 t.interrupt(); } catch (SecurityException ignore) { } finally { // 釋放鎖 w.unlock(); } } if (onlyOne) // 若只中斷一個,則跳出迴圈 break; } } finally { // 釋放鎖 mainLock.unlock(); } }
說明:此函式將會中斷正在等待任務的空閒worker。
shutdownNow函式與shutdown函式相似,shutdownNow會嘗試停止所有的活動執行任務、暫停等待任務的處理,並返回等待執行的任務列表,但是其會終止所有的worker,而並非空閒的worker。
對於其他的函式,有興趣的讀者可以自行分析,下面通過一個示例來詳細講解ThreadPoolExecutor的內部工作機制。
四、示例
通過上面的分析,對於一些重要的函式有了一個整體的認識,下面通過一個示例,看看這些函式之間是如何串聯起來的,並且分析分析ThreadPoolExecutor的工作機制。
package com.hust.grid.leesf.threadpool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; public class FixedThreadPoolDemo { public static void main(String[] args) throws InterruptedException { ExecutorService es = Executors.newFixedThreadPool(2); MyRunnable mr1 = new MyRunnable(10, "mr1"); MyRunnable mr2 = new MyRunnable(5, "mr2"); MyRunnable mr3 = new MyRunnable(10, "mr3"); es.submit(mr1); es.submit(mr2); es.submit(mr3); es.shutdown(); } static class MyRunnable implements Runnable { private int count; private String name; public MyRunnable(int count, String name) { this.count = count; this.name = name; } public void run() { for (int i = 0; i < count; i++) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(name); } } } }
執行結果(某一次)
mr1
mr2
mr2
mr1
mr2
mr1
mr1
mr2
mr2
mr1
mr3
mr1
mr3
mr1
mr3
mr1
mr1
mr3
mr3
mr1
mr3
mr3
mr3
mr3
mr3
說明:在程式中,使用了一個FixedThreadPool執行緒池(即corePoolSize與maximumPoolSize相等,且為2),之後線上程池提交了3個執行緒(Runnalbe物件),之後呼叫了shutdown來關閉執行緒池。
① 執行es.submit(mr1),其主要的函式呼叫如下
說明:在呼叫了es.submit(mr1)後,最終執行緒池中會新建一個worker,並且此時workQueue阻塞佇列為空(沒有元素),並且值得注意的是,在runWorker函式中,有一個while迴圈,當某個任務完成後,會從workQueue阻塞佇列中取下一個任務。
② 執行es.submit(mr2),其主要的函式呼叫與執行es.submit(mr1)相同,但是此時的執行緒池狀態有所不同,其狀態如下
說明:此時,執行緒池會有兩個worker,兩個worker會分別封裝mr1和mr2,並且workQueue阻塞佇列還是為空(沒有元素)。
③ 執行es.submit(mr3),其主要的函式呼叫如下
說明:此時,由於執行緒池的worker的數量已經達到了corePoolSize大小,所以,此時會將mr3放入到workQueue阻塞佇列中,此時,執行緒池還是隻有兩個worker,並且阻塞佇列已經存在一個mr3元素。
④ mr2定義的邏輯執行完成,則會從workQueue中取下一個任務(mr3)。主要的函式呼叫如下(從runWorker開始)
說明:此時,會執行使用者再mr3中自定義的邏輯。此時,執行緒池中還是有兩個worker,並且workQueue的大小為0,沒有元素。
⑤ mr1定義的邏輯執行完成,則還是會從workQueue中取下一個任務(null)。主要的函式呼叫如下(從runWorker開始)
說明:此時,由於是阻塞佇列,並且佇列中沒有元素,所以呼叫take會使當前執行緒(worker對應的Thread)被阻塞。
⑥ mr3定義的邏輯執行完成,其過程和mr1完成時相同,會使另外一個worker對應的Thread被阻塞。
⑦ 執行es.shutdown,則主要的函式呼叫如下
說明:在執行shutdown後,會中斷兩個worker對應的Thread執行緒。由於中斷了worker對應的Thread執行緒,則之前由於take操作(響應中斷)而阻塞也會被中斷。
⑧ 其中一個worker對應的執行緒響應中斷,從getTask函式開始(因為在getTask中被阻塞)。
說明:此時,在getTask函式中,會將workerCount的值減一,並且返回null。接著在runWorker函式中退出while迴圈,並進入processWorkerExit函式進行worker退出執行緒池的處理,之後會再次呼叫addWorker,但是此時,不會新增成功。此時,執行緒池只有一個worker,並且workQueue的大小還是為0。
⑨ 另外一個worker對應的執行緒響應中斷,從getTask函式開始(因為在getTask中被阻塞)。與上一個worker的處理過程相同,不再累贅。執行緒池的狀態如下
說明:之後整個程式就執行結束了,最後的狀態為workQueue阻塞佇列大小為0,執行緒池沒有worker,workerCount為0。
最後,給出ThreadPoolExecutor的示意圖
說明:使用者自定義的任務會進入阻塞佇列或者直接進入執行緒池(進入執行緒池後,新建執行緒直接執行),worker會從阻塞佇列中不斷的取任務,直到阻塞佇列中沒有任務。
關於ThreadPoolExecutor還有如下幾點需要注意的
① corePoolSize,表示核心大小,如果執行的執行緒少於 corePoolSize,則建立新執行緒來處理請求,即使其他輔助執行緒是空閒的。
② maxPoolSzie,表示阻塞佇列的大小,如果執行的執行緒多於 corePoolSize 而少於 maximumPoolSize,則僅當阻塞佇列滿時才建立新執行緒。如果設定的 corePoolSize 和 maximumPoolSize 相同,則建立了固定大小的執行緒池(如本例的FixThreadPool)。如果將 maximumPoolSize 設定為基本的無界值(如 Integer.MAX_VALUE),則允許池適應任意數量的併發任務。
③ largestPoolSize,表示曾經同時存在線上程池的worker的大小,為workers集合(維護worker)的大小。
④ 關於shutdown函式和shutdownNow函式的區別,shutdown會設定執行緒池的執行狀態為SHUTDOWN,並且中斷所有空閒的worker,由於worker執行時會進行相應的檢查,所以之後會退出執行緒池,並且其會繼續執行之前提交到阻塞佇列中的任務,不再接受新任務。而shutdownNow則會設定執行緒池的執行狀態為STOP,並且中斷所有的執行緒(包括空閒和正在執行的執行緒),在阻塞佇列中的任務將不會被執行,並且會將其轉化為List<Runnable>返回給呼叫者,也不再接受新任務,其不會停止使用者任務(只是發出了中斷訊號),若需要停止,需要使用者自定義停止邏輯。
五、總結
ThreadPoolExecutor是執行緒池框架的一個核心類,通過對ThreadPoolExecutor的分析,可以知道其對資源進行了複用,並非無限制的建立執行緒,可以有效的減少執行緒建立和切換的開銷,關於ThreadPoolExecutor的原始碼就分析到這裡,有疑問的讀者歡迎交流,謝謝各位園友的觀看~