類繼承的結構
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OYI3uqsH-1574346852727)(…/…/…/…/uploads/javasource/util/executor.jpeg)]
Executor是最頂層的接口,定義了execute(Runnable runnable)方法。ExecutorService繼承了Executor,繼承了execute方法,還定義很多接口方法,例如shutdown、isTerminated、submit等方法。
在下面一層是AbstractExecutorService,這是一個抽象類,實現一些很有用的方法供子類使用。
ThreadPoolExecutor是我們線程池的實現。
ThreadPoolExecutor
ThreadPoolExecutor實現了一個線程池需要的各個方法,它實現了任務提交、線程管理、監控等等方法。
我們還可以在它的基礎上進行擴展,比如實現定時任務的類 ScheduledThreadPoolExecutor(用來在給定延時後執行異步任務或者週期性執行任務) 就繼承自 ThreadPoolExecutor。
構造函數參數
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
我們創建一個線程池參數最全的構造方法如上,這些是我們最關心的參數。
- corePoolSize 核心線程數
- maximumPoolSize 最大線程數
- keepAliveTime 空閒線程的存活時間
- unit 存活時間的單位
- workQueue 任務隊列,BlockingQueue 接口的某個實現(常使用 ArrayBlockingQueue 和 LinkedBlockingQueue)。
- threadFactory 用於創建線程,一般都使用默認的(Executors.defaultThreadFactory()),我們可以通過它將我們的線程的名字設置得比較可讀一些,如 Message-Thread-1, Message-Thread-2 類似這樣。
- handler 拒絕策略
狀態流轉
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;
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;
COUNT_BITS 設置爲 29(32-3),前三位用於存放線程狀態,後29位用於存放線程數
CAPACITY = 2^29-1=536870911 即線程池的最大數量
RUNNING 111 00000000000000000000000000000 接受新的任務,處理等待隊列中的任務
SHUTDOWN 000 00000000000000000000000000000 不接受新的任務提交,但是會繼續處理等待隊列中的任務
STOP 001 00000000000000000000000000000 不接受新的任務提交,不再處理等待隊列中的任務,中斷正在執行任務的線程
TIDYING 010 00000000000000000000000000000 所有的任務都銷燬了,線程池的狀態在轉換爲 TIDYING 狀態時,會執行鉤子方法 terminated()
TERMINATED 011 00000000000000000000000000000 terminated() 方法結束後,線程池的狀態就會變成這個
- RUNNING -> SHUTDOWN:當調用了 shutdown() 後,會發生這個狀態轉換,這也是最重要的
- (RUNNING or SHUTDOWN) -> STOP:當調用 shutdownNow() 後,會發生這個狀態轉換,這下要清楚 shutDown() 和 shutDownNow() 的區別了
- SHUTDOWN -> TIDYING:當任務隊列和線程池都清空後,會由 SHUTDOWN 轉換爲 TIDYING
- STOP -> TIDYING:當任務隊列清空後,發生這個轉換
- TIDYING -> TERMINATED:這個前面說了,當 terminated() 方法結束後
Worker
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
final Thread thread;
Runnable firstTask;
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
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是線程池中的內部類,是線程池中真正執行任務的線程,Worker繼承了AbstractQueuedSynchronizer(aqs)實現了Runable。
Worker中thread是真正的線程;firstTask是在創建線程的時候,如果同時指定了這個線程起來以後需要執行的第一個任務,那麼第一個任務就是存放在這裏的;completedTasks 存放了此線程完成的任務數
構造函數傳入firstTask,也可以傳 null。run方法調用了外部類的runWorker方法。其餘的方法用 AQS 操作,來獲取這個線程的執行權,用了獨佔鎖。
runWorker
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
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;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
這個方法由worker線程啓動後調用,如果指定了該worker的firstTask,則先執行這個任務,之後通過while循環從隊列中取任務。
首先lock,然後判斷線程池狀態大於等於 STOP,那麼意味着該線程也要中斷。
beforeExecute方法是一個鉤子方法,留給需要的子類進行實現;然後執行任務;afterExecutey也是鉤子方法,將task和異常作爲參數,留給子類實現使用;最後將task置爲空,準備getTask,worker的完成任數加1,釋放獨佔鎖。
如果能執行到最後的finally(對線程池進行關閉)有兩種可能 1 getTask 返回 null,也就是說,隊列中已經沒有任務需要執行了,執行關閉。2 任務執行過程中發生了異常。
getTask
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//檢查狀態
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//減少工作線程的數量,返回null CAS操作
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
//核心線程是否超時回收 | 線程數大於核心線程數
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 線程數大於最大線程數,因爲有可能開發者調用了 setMaximumPoolSize() 將線程池的 maximumPoolSize 調小了,那麼多餘的 Worker 就需要被關閉
// 並且 線程數 > 1 獲取隊列爲King,
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
//減少線程數 返回空
if (compareAndDecrementWorkerCount(c))
return null;
//CAS
continue;
}
try {
// 到 workQueue 中獲取任務
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
此方法有三種可能:
- 阻塞直到獲取到任務返回。()默認 corePoolSize 之內的線程是不會被回收的,它們會一直等待任務)
- 超時退出。keepAliveTime 起作用的時候,也就是如果這麼多時間內都沒有任務,那麼應該執行關閉
- 如果發生了以下條件,此方法必須返回 null:
- 池中有大於 maximumPoolSize 個 workers 存在(通過調用 setMaximumPoolSize 進行設置)
- 線程池處於 SHUTDOWN,而且 workQueue 是空的,前面說了,這種不再接受新的任務
- 線程池處於 STOP,不僅不接受新的線程,連 workQueue 中的線程也不再執行
execute 方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//當前線程數少於核心線程數,那麼直接添加一個 worker 來執行任務,創建一個新的線程,並把當前任務 command 作爲這個線程的第一個任務(firstTask)
if (workerCountOf(c) < corePoolSize) {
//添加任務成功,那麼就結束了,返回 false 代表線程池不允許提交任務
if (addWorker(command, true))
return;
c = ctl.get();
}
//如果線程池處於 RUNNING 狀態,把這個任務添加到任務隊列 workQueue 中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//如果線程池已不處於 RUNNING 狀態,那麼移除已經入隊的這個任務,並且執行拒絕策略
if (! isRunning(recheck) && remove(command))
reject(command);
//線程池還是 RUNNING 的,並且線程數爲 0,那麼開啓新的線程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//如果失敗,說明當前線程數已經達到 maximumPoolSize,執行拒絕策略
else if (!addWorker(command, false))
reject(command);
}
執行的流程很簡單,如果當前線程數少於核心線程數直接add一個worker執行,如果大於等於核心線程數會加進任務隊列等待worker執行,如果任務隊列滿了之後,繼續添加worker執行,如果此時線程數超過最大線程數就會執行拒絕策略。
拒絕策略
CallerRunsPolicy
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
如果線程池沒有被關閉,那麼由提交任務的線程自己來執行這個任務。
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
線程池的默認策略,直接拋出異常。
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
不處理直接忽略。
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
把隊列隊頭的任務幹掉,然後提交這個任務。