線程池基本
一、優勢
- 避免頻繁的創建和銷燬線程
- 提供運行效率
- 合理設置線程池大小,避免因線程數超過硬件資源瓶頸帶來的問題
二、類型
- Executors.newFixedThreadPool() 創建固定線程長度的線程池
- Executors.newCachedThreadPool() 創建不限個數的線程池,空閒線程會在 60s 後被回收
- Executors.newSingleThreadExecutor() 創建只有一個線程的線程池,只有一個工作線程在工作,FIFO
- Executors.newScheduledThreadPool() 創建一個可以指定數量的線程池,但這個帶有延遲性和可以週期性執行任務的功能
- Executors.newWorkStealingPool()
三、使用
public class Demo implements Runnable {
@Override
public void run() {
System.err.println("哈哈哈哈");
}
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3);
for (int i = 0 ; i<= 3; i++) {
service.execute(new Demo());
}
}
}
四、ThreadPool 分析
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
我們可以發現,大部分的線程池的創建操作都是通過 new ThreadPollExecutor 進行創建的,我們找到它的構造函數
public ThreadPoolExecutor(int corePoolSize, // 核心線程數
int maximumPoolSize, // 最大線程數
long keepAliveTime, // 線程的空閒時間,超過時間後將會被回收
TimeUnit unit, // 線程超時的時間單位
BlockingQueue<Runnable> workQueue, // 阻塞隊列
RejectedExecutionHandler handler) { // 拒絕策略
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
4.1 newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 在構建該線程池的時候,會傳遞一個線程數,這個傳遞過來的值,代表了核心線程數和最大線程數,通過使用 LinkedBlockingQueue 隊列來進行線程的存放,最大容量是 Integer.MAX_VALUE,相當於沒有上限
- 執行流程
- 首先判斷當前的工作線程是否少於核心線程數
- 如果大於核心線程數,將其放入阻塞隊列中
- 如果少於核心線程數,則直接創建 Worker 進行執行
- 當線程執行完畢,從阻塞隊列中拉取,執行
- 適用於負載比較大的場景,但爲了資源的合理利用,要合理的設置線程數
4.2 newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- cachedThreadPool ,默認創建一個可緩存的線程池,keepAliveTime 設置爲 60,代表線程空閒 60S 之後,就會被回收
- 執行流程
- 沒有核心線程,會直接將任務放到 SynchronousQueue 中
- 如果有空閒線程則直接運行,若沒有空閒線程,則新建一個
- 執行完畢後的線程會繼續接任務,如果60S後還沒有接到任務,則會被直接回收
4.3 newSingleThreadExecutor
簡單的創建一個 先進先出的單線程的線程池
五、執行流程
當我們調用 execute 後
5.1 execute
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
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 的話可以說是一個標記爲
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; // 右移29位-1表示最大線程容量
// 高3位表示運行狀態,
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; // 所有任務執行完畢,線程池工作線程數量爲0,等待調用 terminated()
private static final int TERMINATED = 3 << COUNT_BITS; // terminated() 方法執行完畢
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
通過對 int 值進行位運算,讓高3位表示線程狀態,低29位表示線程數
5.2 addWorker
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//
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(); // 檢查一下狀態,若狀態發生改變,則重新執行
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false; // 是否成功啓動
boolean workerAdded = false; // 是否成功添加
Worker w = null;
try {
w = new Worker(firstTask); // 將當前任務封裝爲 工作線程(Worker)
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()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w); // 添加到工作隊列中,HashSet
int s = workers.size(); // 獲取當前隊列大小
if (s > largestPoolSize) // largestPoolSize 是出現過得最大數
largestPoolSize = s;
workerAdded = true; // 標記成功
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); // 啓動
workerStarted = true; // 標記啓動成功
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w); // 添加失敗
}
return workerStarted;
}
主要實現兩個功能:
- 添加工作線程數,計數
- 創建工作線程,並調用 start 方法啓動
5.3 Worker
private final class Worker extends AbstractQueuedSynchronizer
implements Runnable{
final Thread thread; // 當前線程
Runnable firstTask; // 首先要執行的任務
volatile long completedTasks; // 計數
Worker(Runnable firstTask) {
setState(-1); // 初始狀態 -1,防止在調用 runWorker() 方法前被中斷
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
}
其實這裏面邏輯也比較簡單,Worker 類繼承了 AQS,實現了 Runnable ,最終的執行是調用 runWroker() 方法,
當新建一個 worker 的時候,首先設置一個標記爲 state = -1, 通過線程工廠創建出來線程,將最初要執行的task 設置爲當前 task
5.4 addWorkerFailed
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
// 如果 worker 構建好了,就移除
workers.remove(w);
// 原子遞減
decrementWorkerCount();
// 嘗試結束線程池
tryTerminate();
} finally {
mainLock.unlock();
}
}
5.5 runWorker
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 解鎖爲了允許進行中斷,因爲 new Worker 默認的 state=-1,將 state 設置爲0,只有爲0才能進行中斷
boolean completedAbruptly = true;
try {
// 如果 task 等於空,通過 getTask() 從阻塞隊列中取
while (task != null || (task = getTask()) != null) {
w.lock();
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(); // 調用 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;// 設置爲 Null , 繼續從阻塞隊列中取值
w.completedTasks++; // 計數
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 將 worker 移除,計數遞減
// 根據布爾值 allowCoreThreadTimeOut 來決定是否補充新的 Worker線程
processWorkerExit(w, completedAbruptly);
}
}
5.6 getTask
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// timed 變量用於判斷是否需要進行超時控制。
// allowCoreThreadTimeOut 默認是 false,也就是核心線程不允許進行超時;
// wc > corePoolSize,表示當前線程池中的線程數量大於核心線程數量;
// 對於超過核心線程數量的這些線程,需要進行超時控制
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 如果需要控制超時,則通過 poll 進行獲取
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r; //如果拿到的任務不爲空,則直接返回給 worker 進行處理
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
其實簡單來說,如果當前的工作線程大於核心線程數,並小於最大線程數,且阻塞隊列已經滿了,這時還是可以增加工作線程,但這時如果超時沒有獲取到任務,就代表阻塞隊列已經空了,也就代表可以清除核心線程之外的工作線程,當getTask 爲null的時候,會跳出循環,runWorker 方法執行完畢,由JVM對線程進行回收
六、拒絕策略
默認的拒絕策略爲 : AbortPolicy
-
AbortPolicy : 拋出異常
-
CallerRunsPolicy : 通過當前線程,直接運行
-
DiscardOldestPolicy :丟棄阻塞隊列中靠最前的任務,並執行當前任務
-
DiscardPolicy :不作爲
我們可以通過實現 RejectedExecutionHandler , 實現自己的拒絕策略
七、線程池的基本操作
通過 Executors 進行線程池的創建,因爲好多參數我們不需要去知道是什麼意思,所以就可能會導致出現各種各樣的問題,所以我們可以通過 new ThreadExecutor 的方式去進行創建
-
當我們創建出來線程池的時候,裏面是沒有線程的,我們可以通過 prestartCoreThread,prestartAllCoreThread,一個初始化一個核心線程,一個初始化全部核心線程
-
當我們要關閉線程池的時候,shutdown,shutdownNow,一個調用之後,會執行完畢纔會關閉,一個會停止執行,清空緩存隊列,返回尚未執行完畢的任務
-
我們可以通過 setCorePoolSize() , setMaximumPoolSize(),一個設置核心池大小,一個設置最大線程數
八、阻塞隊列
workQueue 中存放待執行的任務,類型爲 BlockingQueue,通常可取:
-
ArrayBlockingQueue:基於數組的先進先出隊列,此隊列創建時必須指定大小;
-
LinkedBlockingQueue:基於鏈表的先進先出隊列,如果創建時沒有指定此隊列大小,則默 Integer.MA_VALUE;
-
SynchronousQueue:這個隊列比較特殊,它不會保存提交的任務,而是將直接新建一個 線程來執行新來的任務。
九、execute 與 submit 的區別
-
execute
- execute 接收 Runnable 類型參數
- execute 執行報錯會拋出異常
- execute 無返回值
-
submit
- 可以接收 Runnable 和 Callable 兩種類型的參數
- submit 執行報錯不會拋出異常,除非調用 Future.get
- 若傳入的是 Callable 類型的參數,則可以獲得一個 Future 類型的返回值
十、Callable 和 Future
-
實例
@Override public String call() throws Exception { return "hello world"; } public static void main(String[] args) throws ExecutionException, InterruptedException { Demo callableDemo = new Demo(); FutureTask futureTask = new FutureTask(callableDemo); new Thread(futureTask).start(); System.out.println(futureTask.get()); }
public FutureTask(Callable<V> callable) { if (callable == null) throw new NullPointerException(); this.callable = callable; this.state = NEW; // ensure visibility of callable }
RunnableFuture 是一個接口,實現了 Future 和 Runnable
Future 的話表示一個任務的生命週期,並提供相關的判斷,來查看線程執行的一些狀態
public interface Future<V> {
// 取消
boolean cancel(boolean mayInterruptIfRunning);
// 判斷是否取消
boolean isCancelled();
// 判斷是否結束
boolean isDone();
// 獲取,如果當前線程還沒執行完畢,則等待執行完畢
V get() throws InterruptedException, ExecutionException;
// 有超時的獲取
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask 可以說是 Future 和 Runnable 的結合,我們可以將 Runnable 看作是生產者,Future 當做是消費者,生產者通過 run 方法計算結果,消費者通過 get 獲取結果
Future 有一些狀態值
private static final int NEW = 0;// 新建狀態,表示這個 FutureTask 還沒有開始運行
private static final int COMPLETING = 1;// 完成狀態,表示 FutureTask 任務已經計算完成了
private static final int NORMAL = 2;// 正常執行完畢
private static final int EXCEPTIONAL = 3;// 執行完畢,有異常
private static final int CANCELLED = 4;// 執行完畢,被取消
private static final int INTERRUPTING = 5;// 執行完畢,發起了中斷請求
private static final int INTERRUPTED = 6;// 執行完畢,已經完成中斷請求
-
run
public void run() { // 如果當前不是昔年狀態,且設置運行標識失敗,則直接 return // 保證了只有一個線程可以執行 if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; // 設置 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); } }
-
get
public V get() throws InterruptedException, ExecutionException { int s = state; if (s <= COMPLETING) s = awaitDone(false, 0L); return report(s); }
get方法就是阻塞獲取線程執行結果,這裏主要做了兩個事情
- 判斷當前的狀態,如果狀態小於等於 COMPLETING,表示 FutureTask 任務還沒有完結, 所以調用awaitDone方法,讓當前線程等待。
-
- report返回結果值或者拋出異常
-
awaitDone
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; // 設置爲 Null return s; } else if (s == COMPLETING) // cannot time out yet Thread.yield(); else if (q == null) // 構建一個 waitNode ,加入到隊列中 q = new WaitNode(); 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); } }
run 方法執行完畢後會進行釋放