前言
上一篇咱們分析了線程池的架構和它的工作流程,並且最後創建了一個線程池,本篇咱們就來深入分析線程池的實現類ThreadPoolExecutor。
1、構造方法
構造方法中有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.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;
}
2、飽和策略
上面的構造方法中,我們着重需要注意的是飽和策略,線程池中定義了四種飽和策略:
1、CallerRunsPolicy
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
//使用主線程執行新任務
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
//此方法相同於同步方法
r.run();
}
}
}
2、 AbortPolicy(線程池默認的策略)
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
//拋出 RejectedExecutionException來拒絕新任務的處理
throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
}
}
3、DiscardPolicy
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
//不執行任何操作,丟棄新任務
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
4、DiscardOldestPolicy
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
//此策略將丟棄最早的未處理的任務
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
3、阻塞隊列
上一篇例子中,咱們使用Executors工具類創建了一個線程固定大小的線程池,方法內部是使用ThreadPoolExecutor創建的線程池,咱們看下源碼:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
使用的是LinkedBlockingQueue作爲阻塞隊列,LinkedBlockingQueue的默認構造函數允許的隊列長度是Integer.MAX_VALUE,若堆積大量的請求,可能會造成OOM,此處就是爲什麼《阿里巴巴 Java 開發手冊》中不推薦使用Executors工具類創建線程池的原因,要求使用 ThreadPoolExecutor 構造函數的方式,讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
4、execute方法
下面是執行流程圖:
對照流程圖,我們再來看源碼:
//ctl中存放的是int值,int值得高低位保存了線程池運行的狀態和有效線程的數量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int workerCountOf(int c) {
return c & CAPACITY;
}
//任務隊列
private final BlockingQueue<Runnable> workQueue;
public void execute(Runnable command) {
//如果任務爲null,則拋出異常
if (command == null)
throw new NullPointerException();
//獲取線程池狀態和有效線程數
int c = ctl.get();
//以下有3步:
//步驟1:
//如果線程池工作的線程小於核心線程數
if (workerCountOf(c) < corePoolSize) {
//則增加一個線程,並把該任務交給它去執行
if (addWorker(command, true))
//成功則返回
return;
//這裏說明創建核心線程失敗,需要再次獲取臨時變量c
c = ctl.get();
}
//步驟2:
// 走到這裏說明創建新的核心線程失敗,也就是當前工作線程數大於等於corePoolSize
// 線程池的運行狀態是RUNNING,並且嘗試將新任務加入到阻塞隊列,成功返回true
if (isRunning(c) && workQueue.offer(command)) {
//進入到這裏,是已經向任務隊列投放任務成功
//再次獲取線程池狀態和有效線程數
int recheck = ctl.get();
//如果線程池狀態不是RUNNING(線程池異常終止了),將線程從工作隊列中移除
if (! isRunning(recheck) && remove(command))
//執行飽和策略
reject(command);
// 走到這裏說明線程池狀態可能是RUNNING
// 也可能是移除線程任務失敗了(失敗的最大的可能是已經執行完畢了)
//因爲所有存活的工作線程有可能在最後一次檢查之後已經終結,所以需要二次檢查線程池工作線程的狀態
//這裏博主也是看了半天,大家好好體會下
else if (workerCountOf(recheck) == 0)
//若當前線程池工作線程數爲0,則新建一個線程並執行
addWorker(null, false);
}
//步驟3:
// 如果任務隊列已滿,就需要創建非核心線程
// 如果新建非核心線程失敗,則執行飽和策略
else if (!addWorker(command, false))
reject(command);
}
上面的方法多次調用了addWorker方法,我們跟蹤進去看下源碼:
// 添加工作線程,返回true則創建和啓動工作線程成功;返回false則沒有新創建工作線程
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
//獲取線程池對應的int值
int c = ctl.get();
//獲取線程池狀態
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//獲取工作線程數
int wc = workerCountOf(c);
//工作線程數超過允許的“最大線程數”則返回false
//core爲true,“最大線程數”就是核心線程數,則表明創建核心線程數失敗
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 成功通過CAS更新工作線程數wc,則break到最外層的循環
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 如果線程的狀態改變了就跳到外層循環執行
if (runStateOf(c) != rs)
continue retry;
//如果CAS更新工作線程數wc失敗,則可能是併發更新導致的失敗,繼續在內層循環重試即可
// else CAS failed due to workerCount change; retry inner loop
}
}
// 標記工作線程是否啓動成功
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);
/更新當前工作線程的峯值容量largestPoolSize
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
//釋放鎖
mainLock.unlock();
}
//如果加入線程池成功
if (workerAdded) {
//啓動線程
t.start();
workerStarted = true;
}
}
} finally {
//如果線程啓動失敗,則需要從工作線程集合移除對應線程
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
5、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();
}
結束語
本篇詳細的分析了ThreadPoolExecutor的execute方法,耗費了不少時間。如果本文對你哪怕是有一點點的幫助,博主就感覺值了。