上一篇中我們主要對java線程池的概念,線程池中幾個重要參數以及常用的四種線程池做了簡單的介紹,今天我們就從源碼入手,深入分析線程池是怎麼玩的。
一、Executor架構
請看圖:
1.Executor是一個接口,它是Executor框架的基礎,它將任務的提交與任務的執行分離開來。
2.ThreadPoolExecutor是線程池的核心類,用來執行被提交的任務。
3.ScheduledThreadPoolExecutor是一個實現類,可以在給定的延遲後運行命令,或者定期執行命令,該類功能比Timer更強大。
4.Future接口和實現Future接口的FutureTask類,代表異步計算的結果。
5.Runnable接口和Callable接口的實現類,都可以被ThreadPoolExecutor或ScheduledThreadPoolExecutor執行。
二、Executor框架使用示意圖
由圖可以看出來,主線程首先需要創建實現Runnable或者Callable接口的任務對象。工具類Executors可以通過下面兩個方法 把一個Runnable對象封裝成一個Callable接口對象:
(1)Executors.callable(Runnable task)
(2)Executors.callable(Runnable task, Object result)
然後可以把Runnable對象直接交給ExecutorService執行ExecutorService.execute(Runnable command);或者也可以把Runnable對象或Callable對象提交給ExecutorService 執行 ExecutorService.submit(Runnable task) 或 ExecutorService.submit(Callable<T>task)。
如果執行ExecutorService.submit(…),ExecutorService 將返回一個實現 Future 接口的對象(到目前爲止的JDK中,返回的是FutureTask對象)。由於FutureTask實現了Runnable,程序員也可以創建FutureTask,然後直接交給ExecutorService執行。
最後,主線程可以執行 FutureTask.get() 方法來等待任務執行完成。主線程也可以執行
FutureTask.cancel(boolean mayInterruptIfRunning)來取消此任務的執行。
引自:https://blog.csdn.net/lxk_1993/article/details/90672654
三、源碼分析
1.ThreadPoolExecutor
該類是線程池的核心類,繼承自AbstractExecutorService類(該類實現了ExecutorService接口)
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;
該類成員變量ctl使用高3位表示線程池的運行狀態,使用低29位表示線程池中的線程數。
下面我們介紹一下線程池中最最核心的方法,那就是execute方法。我們看看ThreadPoolExecutor的execute方法,代碼如下:
public void execute(Runnable command) {
if (command == 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.
*/
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);
}
從源碼中我們可以看出,當有一個任務提交給線程池的時候,首先會對提交的任務進行非空判斷,保證任務的存在性。如果非空任務提交到線程池,那麼線程池會從成員變量ctl中獲取值,上面我們提到過該成員變量功能很強大,高3位保存了線程池的狀態,低29位保存了線程池中線程的數量。通過workerCountOf()方法獲取到當前線程池中線程的數量,判斷當前線程池中線程數和核心線程數的大小。如果當前線程池中線程數比核心線程池數小,那麼就調用addWorker(Runnable command,true)方法創建一個新線程來執行任務。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 &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
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
}
}
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(w);
}
return workerStarted;
}
從源碼我們可以看出addWorker方法中使用了一個for循環,看起來是一個無限循環,但是在最前面定義了一個retry標記,可以用來終止循環,這個嵌套的for循環主要用來檢查目前的線程池環境能否支持正常創建新的線程來執行任務。如果當前線程池的環境支持正常的創建新線程,那麼就會執行創建新線程的邏輯。
創建新線程的時候會首先創建一個worker對象,把當前任務傳遞給worker對象。在worker對象的構造方法中會使用ThreadFactory創建一個新的線程。ThreadPoolExecutor中維護了一個workers的HashSet用來存放所有的worker對象。
將創建好的worker對象放到workers中。如果該步驟成功,那麼execute方法直接返回,失敗則嘗試進入阻塞隊列。判斷線程池是否爲運行狀態並且任務隊列是否可以繼續存放任務。入隊列成功,檢查線程池狀態,如果狀態部署RUNNING而且remove成功,則拒絕任務。如果當前worker數量爲0,通過addWorker(null, false)創建一個線程,其任務爲nul。如果上述步驟均失敗則嘗試將線程池的數量由corePoolSize擴充至maxPoolSize,如果失敗則拒絕任務。
addWorker共有四種傳參方式。execute使用了其中三種,分別爲:
1.addWorker(paramRunnable, true)
線程數小於corePoolSize時,放一個需要處理的task進Workers Set。如果Workers Set長度超過corePoolSize,就返回false。
2.addWorker(null, false)
放入一個空的task進workers Set,長度限制是maximumPoolSize。這樣一個task爲空的worker在線程執行的時候會去任務隊列裏拿任務,這樣就相當於創建了一個新的線程,只是沒有馬上分配任務。
3.addWorker(paramRunnable, false)
當隊列被放滿時,就嘗試將這個新來的task直接放入Workers Set,而此時Workers Set的長度限制是maximumPoolSize。如果線程池也滿了的話就返回false。