一,ThreadPoolExecutor 概述
1,線程池優勢
在Java中,如果每個請求到達就創建一個線程,創建線程和銷燬線程對系統資源的消耗都非常大,甚至可能比實際業務處理消耗的資源都大。同時,如果在JVM中創建太多的線程,也可能由於過度消耗內存或調度切換從而導致系統資源不足。
爲了解決上面提出的問題,就有了線程池的概念。線程池,就是在一個線程容器中提前放置一定量的初始化線程,如果業務需要創建線程進行業務處理,則直接從線程池中獲取一個線程進行執行,執行完成後歸還線程到線程池,同時線程池也會對創建線程進行限制,不會無休止的創建下去。
使用線程池後,可以後下面幾點優勢:
* 減低創建線程和銷燬線程的開銷
* 提高響應速度,當有新任務需要執行時不需要等待線程創建時就可以直接執行(如果有空閒線程)
* 合理的設置線程池的大小可以避免因爲硬件瓶頸帶來的性能問題
2,類圖
3,線程池常用API
// 線程池初始化
public ThreadPoolExecutor(
// 核心線程數
int corePoolSize,
// 最大線程數
int maximumPoolSize,
// 線程空閒保留時間
long keepAliveTime,
// 線程保留單位
TimeUnit unit,
// 線程任務阻塞容器
BlockingQueue<Runnable> workQueue,
// 線程工廠,一般取默認
ThreadFactory threadFactory,
// 拒絕策略
RejectedExecutionHandler handler);
// 線程執行,不帶返回值
public void execute(Runnable command);
// 線程執行,帶返回值
public <T> Future<T> submit(Callable<T> task);
public <T> Future<T> submit(Runnable task, T result);
public Future<?> submit(Runnable task);
// 關閉線程池
public void shutdown();
4,線程池常量解析,分爲線程池常量和Future常量兩部分
/********************* 線程池常量 **********************/
// 數量爲,該值是29
// 線程池把 Integer 的32位拆分爲高3位和低29位,
// 通過高三位表示狀態,低29位表示正在執行的線程數量,
private static final int COUNT_BITS = Integer.SIZE - 3;
// 最大允許執行的線程數量,因爲高三位表示狀態,所以天然限制爲29位
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 線程池運行狀態
// -1 的二進制是 11111111 11111111 11111111 11111111
// 右移29位就是 11100000 00000000 00000000 00000000
// 高三位表示狀態,所以 RUNNING 的狀態就是 111
private static final int RUNNING = -1 << COUNT_BITS;
// 線程池關閉狀態,不接收新任務,但是執行隊列中的人物
// 狀態爲 0
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 線程池停止狀態,不接受新任務,不執行隊列人物,終止執行任務
// 狀態爲 1
private static final int STOP = 1 << COUNT_BITS;
// 所有任務都已經結束,線程數量爲0,處於該狀態的線程池即將調用 terminated()方法
// 狀態爲 10
private static final int TIDYING = 2 << COUNT_BITS;
// terminated()方法執行完成
// 狀態爲 11
private static final int TERMINATED = 3 << COUNT_BITS;
/********************* Future常量 **********************/
// Future 常量主要是對 state 狀態的幾種情況分析
// NEW 新建狀態,表示這個 FutureTask還沒有開始運行
private static final int NEW = 0;
// COMPLETING 完成狀態,表示 FutureTask 任務已經計算完畢了
// 但是還有一些後續操作,例如喚醒等待線程操作,還沒有完成。
private static final int COMPLETING = 1;
// FutureTask 任務完結,正常完成,沒有發生異常
private static final int NORMAL = 2;
// FutureTask 任務完結,因爲發生異常。
private static final int EXCEPTIONAL = 3;
// FutureTask 任務完結,因爲取消任務
private static final int CANCELLED = 4;
// FutureTask 任務完結,也是取消任務,不過發起了中斷運行任務線程的中斷請求
private static final int INTERRUPTING = 5;
// FutureTask 任務完結,也是取消任務,已經完成了中斷運行任務線程的中斷請求
private static final int INTERRUPTED = 6;
5,JDK提供的幾種常用線程池,阿里開發手冊不提倡用內置的線程池,建議通過構造器自行初始化,後續自行構造的執行流程會分析,該部分參考即可;跟源碼可以發下,各種初始化方式最終也是通過構造器初始化!
// 初始化定長線程池
// 內置指定的核心線程數和最大線程數,並按照線程池流程執行
public static ExecutorService newFixedThreadPool(int nThreads);
// 初始化緩存的線程池
// 沒有核心線程數,默認最大線程數爲 Integer.MAX_VALUE,阻塞隊列爲 SynchronousQueue,不保存數據
// 所有對於緩存的線程池來說,接收一個任務的同時就需要執行一個任務
public static ExecutorService newCachedThreadPool();
// 初始化單例的線程池
// 一次最多執行一個線程任務,多線程競爭時,添加到阻塞隊列等候
public static ExecutorService newSingleThreadExecutor();
// 初始化定時的線程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize);
6,功能DEMO
package com.gupao.concurrent;
import java.util.concurrent.*;
/**
* @author pj_zhang
* @create 2019-10-31 21:56
**/
public class ThreadPoolTest {
private static ThreadPoolExecutor executor =
new ThreadPoolExecutor(20, 20,
0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
public static void main(String[] args) throws ExecutionException, InterruptedException {
Thread thread = new Thread(() -> {
System.out.println("THREAD 線程執行");
});
Runnable runnable = () -> {
System.out.println("RUNNABLE 線程執行");
};
Callable<String> callable = () -> {
Thread.sleep(2000);
return "Callable 線程執行";
};
executor.execute(thread);
executor.execute(runnable);
// 此處打印時間,是爲了演示 Future 的阻塞獲取結果
System.out.println("callable執行前:" + System.currentTimeMillis());
Future<String> future = executor.submit(callable);
String callableResult = future.get();
System.out.println("callable執行後:" + System.currentTimeMillis() + ", " + callableResult);
}
}
二,源碼分析
1,底層方法分析
1.1,ctlOf(int rs, int wc):獲取線程池狀態+數量的 Integer 值,
private static int ctlOf(int rs, int wc) {
return rs | wc;
}
1.2,workerCountOf(int c):獲取工作線程數量
private static int workerCountOf(int c) {
// 根據 ctlOf 獲取到的值,用低29位進行與運算,獲取到線程數量
return c & CAPACITY;
}
1.3,runStateOf(int c):獲取線程狀態
private static int runStateOf(int c) {
return c & ~CAPACITY;
}
2,ThreadPoolExecutor 初始化及執行流程
2.1,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;
}
2.2,線程池執行流程
* 接收到線程任務後,首先判斷核心線程有沒有被全部佔用;沒有被全部佔用,隨機構建一個線程執行任務
* 如果核心線程全部被佔用,查看阻塞隊列是否已滿;如果沒有滿,添加到阻塞隊列
* 如果阻塞隊列已滿,繼續看最大線程數有沒有被全部佔用;如果存在空閒,構建線程執行任務
* 如果最大線程數已經全部佔用,根據定義的拒絕策略進行拒絕操作
2.3,線程池拒絕策略
* 線程池提供了四種拒絕策略,分別是 RejectedExecutionHandler 接口的四種實現
// java.util.concurrent.ThreadPoolExecutor.AbortPolicy
// 異常處理
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
// java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy
// 丟棄最前面的任務,重新添加執行
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
// java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
// 什麼都不做,即丟棄當前任務
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
// java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
// 只要線程池沒有關閉,則直接開線程運行,該策略建議慎用,不收控制
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
// 最後,業務可以自定義拒絕方式,只需要實現 RejectedExecutionHandler 接口,然後重寫其接口方法
3,execute()
* execute(Runnable command):剛纔對線程池的大概執行流程進行了分析,該方法內可以看出代碼實現
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// 獲取狀態位 + 數量位的 int 對象
int c = ctl.get();
// 獲取工作線程數量,首先判斷核心線程數
if (workerCountOf(c) < corePoolSize) {
// 存在空閒的核心線程,進行線程執行
if (addWorker(command, true))
return;
// 如果執行失敗,重新對 c 賦值,說明存在線程競爭
c = ctl.get();
}
// isRunning(c):線程池依舊運行狀態
// workQueue.offer(command):添加到隊列成功
// 次數是判斷加入到阻塞隊列是否成功
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(Runnable firstTask, boolean core)
// Runnable firstTask:當前線程任務
// boolean core:是否核心線程
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
// 獲取線程執行狀態
int c = ctl.get();
// 這部分計算後續搞明白再填充 TODO
int rs = runStateOf(c);
// 線程池已經關閉,不再接受新任務
// SHUTDOWN 狀態不接受新任務,但仍然會執行已經加入任務隊列的任務
// 所以當進入 SHUTDOWN 狀態,而傳進來的任務爲空,並且任務隊列不爲空的時候,是允許添加新線程的,
// 如果把這個條件取反,就表示不允許添加 worker
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;
// 對c遞增,也就是工作線程數遞增
if (compareAndIncrementWorkerCount(c))
break retry;
// 遞增失敗,說明存在線程競爭或者狀態變更,繼續自旋處理
c = ctl.get();
// 此處不等於,說明存在線程池狀態變更
// 等於,說明只是存在線程競爭造成的CAS失敗
if (runStateOf(c) != rs)
continue retry;
}
}
// 上半部分基本是對線程池狀態及工作線程數進行判斷,並最終對工作線程+1,表示當前線程已經搶佔到一個線程位置
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 包裝線程對象爲 Worker 對象
w = new Worker(firstTask);
// 通過 ThreadFactory 構建一個新的線程
final Thread t = w.thread;
if (t != null) {
// 此處加重入鎖
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 繼續獲取線程池狀態進行判斷
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 如果線程已經運行中,則說明存在問題,此處線程還沒有啓用
if (t.isAlive())
throw new IllegalThreadStateException();
// 添加線程包裝後的Worker對象到列表中
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
// 表示工作線程創建成功
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 線程啓動,
// 此處注意,Worker 實現了 Runnable接口,則此處是調用 Worker.run()
t.start();
workerStarted = true;
}
}
} finally {
// 如果添加失敗,則遞減工作線程數
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
* runWorker(Worker w):Worker的 run() 方法內部調用該方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 獲取初始化 Worker時傳遞的線程
// 此處分析的是 execute() 觸發,如果是 submit() 觸發,此處的Runnable實現類應該爲 FutureTask,
// task.run() 最終調用 FutureTask.run()方法,此處會對 Future的狀態進行處理,實現阻塞獲取的功能
Runnable task = w.firstTask;
w.firstTask = null;
// unlock,表示當前 worker 線程允許中斷,因爲 new Worker 默認的 state=-1,
// 此處是調用Worker 類的 tryRelease()方法,將 state 置爲 0,
// 而 interruptIfStarted()中只有 state>=0 才允許調用中斷
w.unlock();
boolean completedAbruptly = true;
try {
// 任務不爲空,則持續執行
// getTask():此處表示不斷從阻塞隊列中獲取元素
while (task != null || (task = getTask()) != null) {
w.lock();
// 線程池狀態爲stop時不接受新任務,並中斷正在執行的人物
// (Thread.interrupted() &&runStateAtLeast(ctl.get(), STOP)確保線程中斷標誌位爲 true 且是 stop 狀態以上,接着清除了中斷標誌
// !wt.isInterrupted()則再一次檢查保證線程需要設置中斷標誌位
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() 的task的不同實現
// 在submit()觸發的功能中,表示FutureTask
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 {
// 將入參 worker 從數組 workers 裏刪除掉;
// 根據布爾值 allowCoreThreadTimeOut 來決定是否補充新的 Worker 進數組workers
processWorkerExit(w, completedAbruptly);
}
}
* getTask():從阻塞隊列中獲取下一個有效任務。線程池定義的超時處理再該部分實現
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 校驗線程池狀態
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 獲取工作線程
int wc = workerCountOf(c);
// 對超時線程進行時間控制
// allowCoreThreadTimeOut默認爲false,表示核心線程不收控制
// wc > corePoolSize:超過核心線程,即最大線程數,則觸發控制
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// timedOut爲true,說明上次阻塞操作已經超時,則工作線程數-1,並返回null
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 此處就是對超時控制的處理,在從隊列中獲取數據時,阻塞獲取
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
// 如果拿到任務,則直接返回去進行處理
if (r != null)
return r;
// 走到這一步,說明超時,在下一步時候進行回收處理
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
* addWorkerFailed(Worker w):添加工作線程失敗
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 從列表中移除當前 Worker
if (w != null)
workers.remove(w);
// 工作線程數遞減
decrementWorkerCount();
// 嘗試修改線程狀態爲 Terminate
tryTerminate();
} finally {
mainLock.unlock();
}
}
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
* reject(Runnable command):拒絕策略
final void reject(Runnable command) {
// 直接執行拒絕策略
handler.rejectedExecution(command, this);
}
4,submit():同時對 Future 進行分析
* submit(Callable<T> task):觸發線程執行
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
// 初始化化一個 FutureTask,實現了 Runnable 接口
RunnableFuture<T> ftask = newTaskFor(task);
// 直接執行 execute 方法
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
* 在 runWorker() 方法中,觸發 task.run(),實際調用的是 Future.run()方法
public void run() {
// 狀態不爲new,直接執行異常
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
// 線程一切正常,準備執行
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 直接調用Callable的call方法,並返回結果
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
// 設置結果
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
* set(V v):設置執行結果
protected void set(V v) {
// 設置狀態爲完成
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
// 設置正常結束
UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
// 完成後續處理,喚醒等待節點
finishCompletion();
}
}
* finishCompletion():該方法主要是喚醒等待節點,去返回結果中拿數據
private void finishCompletion() {
// 獲取等待節點
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
// 等待節點線程優先,直接喚醒
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
// 遞歸下一個等待節點同步處理
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
5,Funture.get()
* get():阻塞獲取數據
public V get() throws InterruptedException, ExecutionException {
// 獲取Future對應的線程執行狀態,如果沒有執行完直接等待
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
// 執行完成後,解析結果集
return report(s);
}
* awaitDone(boolean timed, long nanos)
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
// 線程中斷,直接移除
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
// 此處說明執行完成
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
// 還有後續操作沒有執行完成,暫時讓出時間片段,稍後執行
else if (s == COMPLETING)
Thread.yield();
// 表示狀態爲null,構建等待節點,準備等待
else if (q == null)
q = new WaitNode();
// 使用CAS添加等待節點到等待隊列
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
// 此處表示設置了超時
else if (timed) {
// 如果超時時間沒有獲取到值,則直接退出
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
// 添加等待隊列成功後,線程掛起等待,等待執行完成後進行喚醒,完成那部分已經分析
LockSupport.park(this);
}
}
* report(int s):獲取返回值
private V report(int s) throws ExecutionException {
// 獲取返回值
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}