目錄
參數解釋
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler
)
corePoolSize
核心線程數。在創建了線程池後,線程中沒有任何線程,等到有任務到來時才創建線程去執行任務。默認情況下,在創建了線程池後,線程池中的線程數爲0,當有任務來之後,就會創建一個線程去執行任務,當線程池中的線程數目達到corePoolSize後,就會把到達的任務放到緩存隊列當中。
maximumPoolSize
最大線程數。表明線程中最多能夠創建的線程數量。
keepAliveTime
空閒的線程保留的時間。
TimeUnit
空閒線程的保留時間單位。
BlockingQueue<Runnable>
阻塞隊列,存儲等待執行的任務。參數有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue可選。
ArrayBlockingQueue和PriorityBlockingQueue使用較少,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊策略與BlockingQueue有關。
- 普通的阻塞隊列:如ArrayBlockingQueue,LinkedBlockingQueue,PriorityBlockingQueue等,只要隊列的容量足夠就能成功入隊。
- 其他阻塞隊列:就是SynchronousQueue,它的成功入隊表示有線程同時在接收入隊的數據,有線程能處理入隊數據。這裏留下後文解釋,這個是解線程複用的關鍵。
ThreadFactory
線程工廠,用來創建線程
RejectedExecutionHandler
隊列已滿,而且任務量大於最大線程的異常處理策略。有以下取值
- ThreadPoolExecutor.AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
- ThreadPoolExecutor.DiscardPolicy:也是丟棄任務,但是不拋出異常。
- ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新嘗試執行任務(重複此過程)
- ThreadPoolExecutor.CallerRunsPolicy:由調用線程處理該任務
線程池實現原理
1. 線程池狀態
ThreadPoolExecutor中定義了一個volatile變量,另外定義了幾個static final變量表示線程池的各個狀態:
- volatile int runState;
- static final int RUNNING = 0;
- static final int SHUTDOWN = 1;
- static final int STOP = 2;
- static final int TERMINATED = 3;
runState表示當前線程池的狀態,它是一個volatile變量用來保證線程之間的可見性;
對於四個狀態解釋如下:
- 當創建線程池後,初始時,線程池處於RUNNING狀態;
- 如果調用了shutdown()方法,則線程池處於SHUTDOWN狀態,此時線程池不能夠接受新的任務,它會等待所有任務執行完畢;
- 如果調用了shutdownNow()方法,則線程池處於STOP狀態,此時線程池不能接受新的任務,並且會去嘗試終止正在執行的任務;
- 當線程池處於SHUTDOWN或STOP狀態,並且所有工作線程已經銷燬,任務緩存隊列已經清空或執行結束後,線程池被設置爲TERMINATED狀態。
2. 任務的執行-execute方法
/*
* 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.
*/
/**
谷歌翻譯:
執行一下3個步驟:
1.如果正在運行線程數少於corePoolSize,
嘗試使用傳入Runnable作爲其第一個任務啓動新線程。
對addWorker的調用以原子方式檢查runState和workerCount,
因此通過返回false來防止在不應該添加線程時發生的錯誤警報。
2.如果任務可以成功如隊列,那麼我們仍然需要
仔細檢查是否應該添加一個線程(因爲自上次檢查後現有的線程已經死亡),
或者自從進入此方法後池關閉了。
所以我們重新檢查狀態,如果必要的話,如果沒有,則回滾入隊,
或者如果沒有,則啓動新的線程。
3.如果我們不能排隊任務,那麼我們嘗試添加一個新線程。
如果失敗,我們知道我們已關閉或飽和,因此拒絕該任務。
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 創建core核心線程-不涉及線程複用。
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);
}
代碼比較短,是addworker方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
/* ctl是一個32位的線程池信息標識變量。
包含兩個概念:
workerCount:表明當前有效的線程數
runState:表明當前線程池的狀態,是否處於
Running,Shutdown,Stop,Tidying,Terminate五種狀態。
*/
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;
}
執行步驟:
1. 進入重試方法塊(retry)
a.判斷線程池是否已經shutdown,是則返回false
b.判斷線程數是否大於了1<<29,是則返回false
c.判斷線程數是否大於 corePoolSize or maximumPoolSize(根據core標誌位區分),是則返回false
d.嘗試增加workerCount,成功則跳出retry
e.重新獲取runstatus,如果不爲rs,則重新retry(a),否則重新嘗試增加workerCount(b)
2. 新建worker對象,傳入入參runnable
a.每個worker新建,都會創建一個線程
3.加鎖
4.同步塊執行:
a.判斷狀態是否爲shutdown,如果是且worker對象線程存活,則拋出異常
b.往workers隊列中添加worker
c.如果隊列長度大於largestPoolSize,則更新largestPoolSize爲隊列長度值
d.啓動worker中的線程:thread.start
5.小結
a.此時還沒有結束。可以看到execute方法是新開線程的,怎麼做到線程複用?
b.thread.start調用的是worker的run方法,run->runWorker方法是線程複用的關鍵
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 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) ||
(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);
}
}
步驟如下:
a.進入while(對於核心線程默認來說是死循環,當前正在運行的線程數超出核心線程,getTask會返回null)(參數allowCoreThreadTimeOut配置爲true,核心線程getTask也會返回null。細節可以自己參閱)
b.獲取worker中的firstTask
c.如果沒有,則調用getTask方法(核心線程被blockingqueue阻塞)
d.獲取到了task,調用其run方法。
e.銷燬task。(task=null)
f.重新獲取task(b)
3. 線程複用大致原理
Worker本身是一個runnable,他自身的執行邏輯是從workQueue中取runnable來執行其run方法。