main方法
先貼上我做實驗使用的main方法
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
int corePoolSize = 2;
int maximumPoolSize = 4;
BlockingQueue<Runnable> blockingDeque = new ArrayBlockingQueue<>(2);
RejectedExecutionHandler handle = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, 0,
TimeUnit.SECONDS, blockingDeque, handle);
Thread[] threads = new Thread[6];
for(int i = 0; i < 6; i ++) {
int finalI = i;
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("start: " + finalI);
Thread.sleep(1000);
System.out.println("end: " + finalI);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
for(Thread thread: threads) {
threadPoolExecutor.submit(thread);
}
System.out.println(threadPoolExecutor.getActiveCount());
threadPoolExecutor.shutdown();
}
}
構造器
這裏只貼一個, 其他構造方法都是直接使用這個的
參數名稱 | 作用 |
---|---|
corePoolSize | 初始線程池的線程個數 |
maximumPoolSize | 線程池中最大線程個數 |
keepAliveTime | 線程存活時間 |
unit | 存活時間的單位 |
workQueue | 一個阻塞隊列 |
threadFactory | 線程工廠 |
handler | 採取的飽和政策(拒絕的策略) |
拒絕策略 | 行爲 |
---|---|
AbortPolicy | 拒絕時會拋出RejectedExecutionException異常 |
DiscardPolicy | 直接忽略 |
DiscardOldestPolicy | 丟棄執行隊列中最老的任務, 嘗試提供位置給當前任務 |
CallerRunsPolicy | 交給任務提交者執行 |
拒絕策略默認爲AbortPolicy, 構造器中前五個參數都是必填參數, 線程工廠默認爲Executors中的defaultThreadFactory
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;
}
Worker內部類
Worker繼承了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;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
// 構造器, 從工廠中獲取新線程
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
// 這個任務的run方法調用在ThreadPoolExecutor中的runWorker, 後面會寫到
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
// 以下都是重寫AQS方法來實現鎖
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) {
}
}
}
}
executor
executor是將任務提交到線程池, 這是使用線程池的第二步, 第一步當然是new一個線程池
這個方法裏面有三個判斷
- 當前正在執行的任務小於corePoolSize, 也就是線程池還有空餘, 那麼直接執行任務, 否則進入2
- 將當前任務放入阻塞隊列, 等待直至線程池中有空閒線程
- 如果阻塞隊列滿了, 就會判斷之前設置的maximumPoolSize最大線程個數, 如果當前執行的任務個數小於maximumPoolSize, 那麼就會創建新線程去執行這個任務
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// ctl初始值是一個很大的負數, 這個負數同CAPACITY進行與運算可以得出正在運行的任務個數
int c = ctl.get();
// 這是第一個判斷
if (workerCountOf(c) < corePoolSize) {
// 在addWorker中通過true和false來判斷這是第一步還是第三步
if (addWorker(command, true))
return;
c = ctl.get();
}
// isRunning方法用於判斷線程池是否還在工作
// 如果還在工作就將任務加入阻塞隊列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 重新進行工作檢測, 在併發情況下可能會出現工作狀況被別的線程修改的情況
// 如果線程池結束工作了並且阻塞隊列中移除當前任務成功, 則進行飽和策略
if (! isRunning(recheck) && remove(command))
reject(command);
// 否則判斷當前正在執行的任務個數是不是0
// 如果是則添加一個空任務
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 阻塞隊列滿了, 第三步
else if (!addWorker(command, false))
// 添加任務失敗則進行飽和策略
reject(command);
}
addWorker
這個方法是線程池中很重要的方法, 用於執行任務
private boolean addWorker(Runnable firstTask, boolean core) {
// break標記
retry:
for (;;) {
int c = ctl.get();
// 獲取當前線程池狀態
int rs = runStateOf(c);
// 如果rs是終止狀態或者其他三種狀態(這些狀態都不接受新任務)並且
// rs不是終止狀態或者傳入的任務爲空, 或者阻塞隊列爲空
// 就會進入語句塊, 返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
// wc是線程正在執行的任務個數
// 如果wc大於線程池所能承受的最大數量CAPACITY = 536,870,911(二進制中29個1)
// 否則, 看是executor的第幾步來判斷是否大於corePoolSize 或者maximumPoolSize
// 如果上述兩個條件滿足一個則返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 使用cas操作將正在執行的任務+1, 成功則跳出最外側循環
if (compareAndIncrementWorkerCount(c))
break retry;
// cas失敗了, 則更新c的數值
c = ctl.get(); // Re-read ctl
// 判斷rs狀態是否改變
if (runStateOf(c) != rs)
continue retry;
// 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 {
// 獲取rs的狀態
int rs = runStateOf(ctl.get());
// 這裏不需要進行跟上面一樣的重複判斷, 因爲這裏是同步的
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 判斷t線程是否已經存在, 因爲這裏需要啓動線程
if (t.isAlive())
throw new IllegalThreadStateException();
// 將w加入到workers中, workers是一個被final修飾的hashSet
// 這裏提一句被final修飾的類或者對象我們是可以改變其內部值的
// final修飾對象時僅僅是不可改變其引用地址
// 因爲workers的所有方法在使用前都會加上鎖, 所以這裏可以直接獲取size
workers.add(w);
int s = workers.size();
// largestPoolSize初始是0, 用於記錄最大的同時執行任務數
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 任務添加成功就執行線程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// 任務執行失敗就調用添加任務失敗方法來移除任務
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
runWorker
在runWorker中已經創建的Worker會一直存在, 並且會自旋, 獲取阻塞隊列中的任務執行就是在這個方法中, 個人理解Worker最大值就是之前設置的最大線程數
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 這裏的循環如果初始傳入的Worker中無任務, 則使用getTask方法從阻塞隊列中獲取
while (task != null || (task = getTask()) != null) {
w.lock();
// 當前線程池是否大於STOP狀態, 或者當前線程需要中斷並清除中斷狀態, 並且
// 這時的線程池狀態是大於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, 該Worker的完成任務數量+1
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// task爲空並且阻塞隊列中無任務則關閉該Worker
processWorkerExit(w, completedAbruptly);
}
}
不難發現, 只要阻塞隊列中還有任務, 已經存在的Worker就不會關閉, 而是去執行阻塞隊列中的任務
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 這個標誌在上文中, 如果是正常的try流程則會爲false
// 如果是異常了就會是true, 這裏就會進入decrementWorkerCount方法
// 使用cas操作將ctl- 1
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 更新總完成數, 在hashSet中移除該Worker
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
// 判斷線程池狀態嘗試終止線程池
tryTerminate();
int c = ctl.get();
// 如果當前線程池狀態是不需要被關閉的, 即小於STOP的
if (runStateLessThan(c, STOP)) {
// 判斷是否正常中止, 如果是則說明所有任務都已完成就進入該語句塊
if (!completedAbruptly) {
// min默認是corePoolSize, 如果允許超時則是0
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 如果min是0阻塞隊列是不爲空的, 那麼必須要保證有一個工作線程
if (min == 0 && ! workQueue.isEmpty())
min = 1;
// 如果當前線程數量大於等於最小工作線程數量則直接返回
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 因爲不是正常中止, 所以該Worker不該被中止, 添加一個空的Worker
// 這樣可以繼續執行阻塞隊列中的任務
addWorker(null, false);
}
}
getTask
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
// 這是一個死循環, 即必須返回一個元素
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 判斷線程池狀況, 如果下面情況同時滿足則將ctl - 1
// 1.狀態>=SHUTDOWN, 即需要被中止
// 2.狀態>=STOP, 即需要被中止並且不完成已添加的任務, 或者阻塞隊列爲空
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 獲取工作線程個數
int wc = workerCountOf(c);
// 判斷工作線程個數是否大於核心線程個數, 這裏的情況是阻塞隊列曾經滿過, 直接開新線程
// allowCoreThreadTimeOut是默認爲false的, 我們假設他這裏也爲false
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 如果工作線程數量大於最大線程數量, 或者
// timed是true並且timedOut是true(這裏timedOut默認爲false)
// 並且工作線程個數大於1或者阻塞隊列爲空
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 滿足條件就將wc-1, 因爲wc大於最大線程數量或者已經超時
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// timed爲true則將阻塞隊列的隊列頭出列, 這裏就是阻塞隊列中的任務獲得過程
// 返回到runWorker的while中執行
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
// 返回r, 可以給Worker執行
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
後記
在學習ThreadPoolExecutor源碼的過程中遇到最大的問題就是, 一直在想阻塞隊列中的任務是如何執行的, 線程池爲什麼可以節省創建線程的時間, 線程池中是否創建的時候就自帶線程, 這些問題在學習完源碼之後就都解決了