幾種常見線程池:
- newScheduledThreadPool
- 創建一個定長線程池,支持定時及週期性任務執行,可以作一個定時器使用。
- newCachedThreadPool
- 創建一個可緩存線程池,如果線程池長度超過需要的線程數量,可靈活回收空閒線程,若無可回收,則新建線程。
- newSingleThreadExecutor
- 創建一個單線程的線程池,它只會用唯一的工作線程來執行任務,保證所有任務按照指定順序(FIFO, LIFO, 優先級)執行,可以控制線程的執行順序。
- newFixedThreadPool
- 創建一個定長線程池,可控制線程最大併發數,超出的線程會在隊列中等待;當創建的線程池數量爲1的時候,類似單線程化的線程池,當爲1的時候,也可控制線程執行順序。
常用阻塞隊列:
-
ArrayBlockingQueue:基於數組的阻塞隊列實現,在ArrayBlockingQueue內部,維護了一個定長數組,生產者放入數據和消費者獲取數據,共用同一個鎖對象,者無法真正並行運行。可以控制對象內部鎖是否公平鎖,默認採用非公平鎖。ArrayBlockingQueue和LinkedBlockingQueue,前者插入或刪除元素時不會產生或銷燬任何額外對象,後者會生成一個額外Node對象。長時間內需要高效併發地處理大批量數據的系統中,其對於GC存在一定的影響。
-
LinkedBlockingQueue:基於鏈表的阻塞隊列,能夠高效的處理併發數據,因其對於生產者端和消費者端分別採用了獨立的鎖來控制數據同步,意味着在高併發的情況下生產者和消費者可以並行操作隊列數據,以提高整個隊列併發性能。沒有指定容量大小,會默認無限大小容量(Integer.MAX_VALUE),如果生產者速度大於消費者速度,系統內存就有可能已被消耗殆盡了。
-
DelayQueue:當其指定的延遲時間到了,才能夠從隊列中獲取到該元素。是一個沒有大小限制的隊列,因此往隊列中插入數據的操作(生產者)永遠不會被阻塞,而只有獲取數據的操作(消費者)纔會被阻塞。
-
PriorityBlockingQueue:基於優先級的阻塞隊列(優先級的判斷通過構造函數傳入的Compator對象來決定),但需要注意的是並不會阻塞數據生產者,而只會在沒有可消費的數據時,阻塞數據的消費者。
-
SynchronousQueue:一種無緩衝的等待隊列。
常用拒絕策略:
- AbortPolicy:丟棄任務並拋出RejectedExecutionException異常。
- DiscardPolicy:丟棄任務,但是不拋出異常。
- DiscardOldestPolicy:丟棄隊列最前面的任務,然後重新提交被拒絕的任務。
- CallerRunsPolicy:由調用線程(提交任務的線程)處理該任務。
線程池源碼解讀:
- ThreadPoolExecutor.execute(Runnable command)詳解
ctl是一個Integer值,它是對線程池運行狀態和線程池中有效線程數量標識字段,共32位,高3位表示"線程池狀態",低29位表示"線程池中的任務數量"
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;
// 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; }
在多線程環境,運行狀態和有效線程數量統一,不能出現一個改而另一個未改的情況;將他們其放AtomicInteger中,利用原子操作,可以保證這兩個值始終是統一的。
// 線程池執行任務的方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// 獲取當前工作線程的數量
int c = ctl.get();
// 1、如果數量小於核心線程池的數量
if (workerCountOf(c) < corePoolSize) {
// 創建核心線程並執行任務
// 添加成功返回
if (addWorker(command, true))
return;
// 執行失敗,重新獲取工作線程數量
c = ctl.get();
}
// 2、如果有效線程數大於等於核心線線程數,且程池是運行狀態,嘗試將任務加入阻塞隊列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 再次檢查線程池的狀態,如果不是運行中,移除剛纔添加的任務
if (! isRunning(recheck) && remove(command))
reject(command);
// 線程池中沒有線程,增加一個線程
// 主要是爲了防止線程池的任務隊列裏有任務而沒有線程可用的這種情況發生。
else if (workerCountOf(recheck) == 0)
//第一個參數爲null,說明只爲新建一個worker線程,沒有指定firstTask
//第二個參數爲true代表佔用corePoolSize,false佔用maxPoolSize
addWorker(null, false);
}
// 3、如果線程池不是running狀態,或者放入阻塞隊列失敗,則嘗試創建新線程執行任務,擴容至maxPoolSize
// 如果創建線程失敗,則調用拒絕策略
else if (!addWorker(command, false))
reject(command);
}
// 創建線程執行任務
// core:true創建核心線程;false創建最大線程
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
/*
作用是判斷ctl變量當前的狀態是否可以添加任務,
特別說明了如果線程池處於SHUTDOWN狀態時,
可以繼續執行阻塞隊列中的任務,
但不能繼續往線程池中添加任務了;
同時增加工作線程數量使用了AQS作同步,
如果同步失敗,則繼續循環執行。
*/
for (;;) {
int c = ctl.get();
// 獲取線程池狀態
int rs = runStateOf(c);
// 如果線程池不再接收新任務
// 線程池狀態爲SHUTDOWN,可以繼續執行阻塞隊列中的任務,不能往線程池添加任務
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;
// AQS增加有效線程數量
if (compareAndIncrementWorkerCount(c))
break retry;
// 再次獲取ctl的值
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類中
Worker w = null;
try {
// 創建一個worker,將當初任務放進去
w = new Worker(firstTask);
//獲取worker中的線程
// 這裏會調用ThreadFactory創建一個新的線程
final Thread t = w.thread;
if (t != null) {
// workers是一個HashSet,操作需要加鎖
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 獲取當前線程池狀態
int rs = runStateOf(ctl.get());
// 如果線程池狀態爲運行中
// 或者線程池狀態爲SHUTDOWN且firstTask爲null,往線程池中添加線程池
// SHUTDOWN時不會添加新的任務,但是會執行阻塞隊列中的任務
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 如果此時線程池是運行態,拋出異常
if (t.isAlive())
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;
}
// 創建一個Worker的構造方法
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
// newThread傳的參數是Worker本身,而Worker實現了Runnable接口
this.thread = getThreadFactory().newThread(this);
}
// 執行t.start()時,執行的是Worker的run()方法
public void run() {
runWorker(this);
}
// 任務執行
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 ((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置爲空,線程可以自行調用getTask()方法從隊列中獲取任務
task = null;
// 記錄當前worker執行多少次任務
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 線程回收的過程
processWorkerExit(w, completedAbruptly);
}
}
// 下面的方法很重要,是獲取任務的方法
private Runnable getTask() {
// 超時標記,默認false,如果調用workerQueue.pull()超時則會置爲true
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.
// 線程池狀態大於SHUTDOWN,且workQueue中的任務爲空
// CAS減少工作線程數量,返回null,線程被回收
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 獲取線程池中有效線程數量
int wc = workerCountOf(c);
// Are workers subject to culling?
// allowCoreThreadTimeOut默認爲false,即默認不允許核心線程超時回收
// 這裏也說明在覈心線程以外的線程都爲“臨時”線程,隨時會被線程池回收
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 現併發時操作了setMaximumPoolSize方法,如果此時將最大線程數量調少了
// 很可能會出現當前工作線程大於最大線程的情況
// 這時就需要線程超時回收,以維持線程池最大線程小於maximumPoolSize
// timed && timedOut
// 如果爲true,表示當前操作需要進行超時控制,這裏的timedOut爲true,說明該線程已經從workQueue.poll()方法超時了
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// CAS線程池數量減一
if (compareAndDecrementWorkerCount(c))
return null;
// 循環重試
continue;
}
try {
// 阻塞超時獲取任務,或者阻塞獲取任務
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
// 如果獲取任務超時,將timeOut設置爲true
// 繼續循環執行,如果碰巧開發者開啓了allowCoreThreadTimeOut,那麼該線程就滿足超時回收了
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
// 如果任務沒有執行,需要進行回滾
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}