筆記文章,沒有廢話,句句關(guān)鍵
線程池的優(yōu)點
- 重用線程池里的線程,避免創(chuàng)建和銷毀線程所帶來的性能開銷
- 有效控制最大并發(fā)數(shù),避免造成線程間搶占系統(tǒng)資源而造成阻塞
- 提高線程可管理性,可以統(tǒng)一進行分配,調(diào)優(yōu)和監(jiān)控的能力
Android中的線程池
復(fù)用Java中的Executor接口,具體實現(xiàn)類為ThreadPoolExecutor,它有以下幾個參數(shù):
參數(shù) | 說明 | 注釋 |
---|---|---|
corePoolSize | 線程池中核心線程數(shù)量 | 一直存活,即使處于閑置狀態(tài) |
maximumPoolSize | 最大能創(chuàng)建的線程數(shù)量(非核心線程,包含核心線程個數(shù)) | 達到這個值后,后續(xù)任務(wù)會阻塞 |
keepAliveTime | 非核心線程最大存活時間 | 當(dāng)設(shè)置allowCoreThreadTimeOut=true 同樣會做用于核心線程,但通常不會這么做 |
unit | keepAliveTime的時間單位 | TimeUnit中的枚舉(時間單位) |
workQueue | 等待隊列。execute 方法提交的Runnable存儲在其中 | 如果線程池中的線程數(shù)量大于等于corePoolSize的時候,把該任務(wù)放入等待隊列 |
threadFactory | 線程創(chuàng)建工程廠(用來創(chuàng)建線程的) | 默認(rèn)使用Executors.defaultThreadFactory() 來創(chuàng)建線程,線程具有相同的NORM_PRIORITY優(yōu)先級并且是非守護線程 |
handler | 線程池的飽和拒絕策略(不常用) | 阻塞隊列已且沒有空閑的線程,此時繼續(xù)提交任務(wù),就需要采取一種策略處理該任務(wù),默認(rèn)會拋出異常 |
常用與關(guān)鍵方法
- void execute(Runnable run)//提交任務(wù),交由線程池調(diào)度
- void shutdown()//關(guān)閉線程池,等待任務(wù)執(zhí)行完成
- void shutdownNow()//關(guān)閉線程池,不等待任務(wù)執(zhí)行完成
- int getTaskCount()//返回線程池找中所有任務(wù)的數(shù)量
(已完成的任務(wù)+阻塞隊列中的任務(wù))
- int getCompletedTaskCount()//返回線程池中已執(zhí)行完成的任務(wù)數(shù)量
(已完成的任務(wù))
- int getPoolSize()//返回線程池中已創(chuàng)建線程數(shù)量
- int getActiveCount()//返回當(dāng)前正在運行的線程數(shù)量
- void terminated() 線程池終止時執(zhí)行的策略
線程池的運行狀態(tài)
線程存在運行狀態(tài),如下:
狀態(tài) | 說明 |
---|---|
NEW | 初始狀態(tài),線程被新建,還沒調(diào)用start方法 |
RUNNABLE | 運行狀態(tài),把"運行中"和"就緒"統(tǒng)稱為運行狀態(tài) |
BLOCKED | 阻塞狀態(tài),表示線程阻塞于鎖 |
WAITING | 等待狀態(tài),需要其他線程通知喚醒 |
TIME_WAITING | 超時等待狀態(tài),表示可以在指定的時間超時后自行返回 |
TERMINATED | 終止?fàn)顟B(tài),表示當(dāng)前線程已經(jīng)執(zhí)行完畢 |
根據(jù)線程的運行狀態(tài)思想,我們來展開線程池的運行狀態(tài):
狀態(tài) | 說明 |
---|---|
RUNNABLE | 表示當(dāng)前線程池,存在正在運行的線程 |
SHUTDOWN | 關(guān)閉線程池,不在執(zhí)行新的任務(wù),但會執(zhí)行完線程池正在運行的任務(wù),和添加到隊列中的任務(wù),對應(yīng)shutDown()方法 |
STOP | 立即關(guān)閉線程池,打斷正在運行的任務(wù),且不再處理等待隊列中已添加的任務(wù),對應(yīng)shutDownNow()方法 |
TIDYING | shutDown() / shutDownNow()后進入此狀態(tài),表示隊列和線程池為空,之后進入TERMINATED狀態(tài) |
TERMINATED | 終止?fàn)顟B(tài),表示當(dāng)前線程已經(jīng)執(zhí)行完畢,并調(diào)用 terminated() 通知外界 |
安卓中常用的四種線程池
Executors.newFixedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- 線程數(shù)量固定的線程池
- 核心線程與非核心線程數(shù)量相等,意味著只有核心線程。
- keepAliveTime = 0L,即使線程池中的線程空閑,也不會被回收。除非調(diào)用shutDown()或shutDownNow()去關(guān)閉線程池。
- 可以更快響應(yīng)外界的請求,且任務(wù)阻塞隊列為無邊界隊列(LinkedBlockingQueue()鏈表結(jié)構(gòu)的阻塞隊列),意味著任務(wù)可以無限加入線程池
Executors.newScheduledThreadPool()
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
10L, MILLISECONDS,
new DelayedWorkQueue());
}
- 核心線程數(shù)量固定,非核心線程是無限的,但非核心線程存活時間非常短(10毫秒),閑置后會被回收
- 適合執(zhí)行定時任務(wù)和固定周期的重復(fù)任務(wù)
- DelayedWorkQueue()優(yōu)先級隊列(堆結(jié)構(gòu)),會根據(jù)定時/延時時間進行排序,延時少,先執(zhí)行
Executors.newSingleThreadExecutor()
常用
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- 核心線程數(shù)為1的線程池。確保所有任務(wù)在同一線程執(zhí)行,因此不用考慮線程同步問題
- 阻塞隊列是無界的,可以一直接收任務(wù) 、
Executors.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- 沒有核心線程,非核心線程無限,但執(zhí)行完任務(wù)后,線程存活60秒
- SynchronousQueue() 是個特殊的隊列,可以理解為無法存儲元素的隊列,因此線程池接收到任務(wù)就會創(chuàng)建線程去執(zhí)行。當(dāng)整個線程都處于空閑狀態(tài),60秒后,線程池中的線程都會因超時而被停止,不占用系統(tǒng)資源。
- 根據(jù)以上兩點,此線程池適合執(zhí)行耗時較少但頻繁的任務(wù)執(zhí)行
線程池源碼分析
在分析源碼前,先把線程池的工作流程圖放出來。這樣再去閱讀源碼會更加深刻
閱讀源碼的目的:
- 了解線程池如何復(fù)用
- 如何巧妙的使用線程池,根據(jù)需求去自定義線程池,以達到暫停/恢復(fù),任務(wù)具有優(yōu)先級,監(jiān)控,分配,調(diào)優(yōu)等
- 學(xué)習(xí)線程池優(yōu)秀的設(shè)計思想
- 面試吹牛逼
源碼分析
分析源碼前看下ThreadPoolExecutor類的頭注釋:
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*
* In order to pack them into one int, we limit workerCount to
* (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
* billion) otherwise representable. If this is ever an issue in
* the future, the variable can be changed to be an AtomicLong,
* and the shift/mask constants below adjusted. But until the need
* arises, this code is a bit faster and simpler using an int.
*
* The workerCount is the number of workers that have been
* permitted to start and not permitted to stop. The value may be
* transiently different from the actual number of live threads,
* for example when a ThreadFactory fails to create a thread when
* asked, and when exiting threads are still performing
* bookkeeping before terminating. The user-visible pool size is
* reported as the current size of the workers set.
*
* The runState provides the main lifecycle control, taking on values:
*
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
*
* The numerical order among these values matters, to allow
* ordered comparisons. The runState monotonically increases over
* time, but need not hit each state. The transitions are:
*
* RUNNING -> SHUTDOWN
* On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
* On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
* When both queue and pool are empty
* STOP -> TIDYING
* When pool is empty
* TIDYING -> TERMINATED
* When the terminated() hook method has completed
*
* Threads waiting in awaitTermination() will return when the
* state reaches TERMINATED.
*
* Detecting the transition from SHUTDOWN to TIDYING is less
* straightforward than you'd like because the queue may become
* empty after non-empty and vice versa during SHUTDOWN state, but
* we can only terminate if, after seeing that it is empty, we see
* that workerCount is 0 (which sometimes entails a recheck -- see
* below).
*/
這里面有幾段比較重要 :
* The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*
* In order to pack them into one int, we limit workerCount to
* (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
* billion) otherwise representable. If this is ever an issue in
* the future, the variable can be changed to be an AtomicLong,
* and the shift/mask constants below adjusted. But until the need
* arises, this code is a bit faster and simpler using an int.
線程池控制狀態(tài):clt
是一個int類型的原子數(shù),它表示兩個概念,workerCount表示有效線程,籠統(tǒng)來講,相當(dāng)于線程池運行的線程個數(shù),runState,表示線程池運行的狀態(tài),如RUNNING SHUTDOWN等等。
將這兩個字段打包成一個int,int為4字節(jié),有32位,后29位表示線程池的數(shù)量,最大可為5個億,如果不夠可以使用AtomicLong
代替。當(dāng)然,運行線程池根本達不到這個數(shù)量。
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
五種狀態(tài)的含義,上面提到過。
* RUNNING -> SHUTDOWN
* On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP
* On invocation of shutdownNow()
* SHUTDOWN -> TIDYING
* When both queue and pool are empty
* STOP -> TIDYING
* When pool is empty
* TIDYING -> TERMINATED
* When the terminated() hook method has completed
線程池狀態(tài)間的變化。等于上面我們畫的圖:
結(jié)論:閱讀源碼可以適當(dāng)看下類頭說明,尤其是Android源碼的類頭。可以幫我們更好的理解源碼
從線程池入口進入,理解前面提到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;
// 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; }
Integer.SIZE為32,因此 COUNT_BITS = Integer.SIZE - 3 = 29 CAPACITY表示線程池的容量:
(1 << COUNT_BITS) - 1 ---> 100000000000000000000000000000(30位) -1 -->11111111111111111111111111111(29位)
這代表int32位
中,前3位表示線程池狀態(tài)
,后面29位表示容量
計算結(jié)果的值如下:
總結(jié)來說,狀態(tài)值自上而下,數(shù)值越來越大
AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
初始狀態(tài)為RUNNING,線程數(shù)為0
ctlOf(int rs, int wc) 用來獲取int中的值,用來調(diào)用下面兩個方法 :
private static int runStateOf(int c) { return c & ~CAPACITY; } 獲取線程池狀態(tài)
private static int workerCountOf(int c) { return c & CAPACITY; } 獲取線程池中線程的有效線程數(shù)量
計算方式如下:
理解上面的計算方式后,開始真正進入源碼閱讀
==入口函數(shù):== Executor.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();
int num=workerCountOf(c);
//第一步
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);
}
// firstTask 待執(zhí)行的任務(wù) core 是否要啟動核心線程
private boolean addWorker(Runnable firstTask, boolean core)
注釋翻譯: 線程池的執(zhí)行分3步 :
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
- 如果小于
corePoolSize
核心線程數(shù),直接創(chuàng)先新的線程,并將任務(wù)作為該線程的第一個任務(wù)(firstTask),因為是往新線程執(zhí)行任務(wù),肯定是第一個任務(wù)addWorker()
方法的返回值,表示是否添加任務(wù)成功,core = true
啟動核心線程 ,并直接return返回,不再向下執(zhí)行
// if (workerCountOf(c) < corePoolSize)不滿足,也就是運行線程大于等于核心線程,
// 此時可能為核心線程達到最大,或核心與非核心共存
// 那么繼續(xù)判斷,線程池為運行狀態(tài),同時加入隊列是否成功,
if (isRunning(c) && workQueue.offer(command)) {
// 成功! 進行二次校驗,因為此時線程池中的線程可能銷毀
// (比如非核心到達keepAliveTime指定存活時間而銷毀)或某個線程調(diào)用shutDown()方法
int recheck = ctl.get();
// 如果不是RUNNING狀態(tài) ,刪除剛才offer進隊列的任務(wù)
if (!isRunning(recheck) && remove(command))
// 執(zhí)行拒絕策略 調(diào)用創(chuàng)建時的handle,執(zhí)行 handler.rejectedExecution(command, this); ,默認(rèn)拋出異常
reject(command);
// 如果線程池 沒有正在運行的線程,那么這個線程可能調(diào)用了shutDown()方法,要關(guān)閉了,那么就加入個null任務(wù),
//也就是不執(zhí)行 新任務(wù),去創(chuàng)建非核心線程 core = false 執(zhí)行等待隊列中的任務(wù)
else if (workerCountOf(recheck) == 0)
// 添加個null任務(wù)
addWorker(null, false);
}//(isRunning(c) && workQueue.offer(command))不滿足,表示等待隊列已滿,添加任務(wù)失敗,那么就創(chuàng)建非核心線程
//如果創(chuàng)建失敗。執(zhí)行拒絕策略
else if (!addWorker(command, false))
reject(command);
boolean addWorker(Runnable firstTask, boolean core)
方法邏輯:
private boolean addWorker(Runnable firstTask, boolean core) {
retry: //雙層for循環(huán) retry外層循環(huán)終止標(biāo)識
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 如果rs >= SHUTDOWN不再添加新執(zhí)行 后面的判斷條件可以不關(guān)心
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//獲取線程池中,正在執(zhí)行任務(wù)的線程
int wc = workerCountOf(c);
// 大于容量,不可能,不用看, wc >= (core ? corePoolSize : maximumPoolSize 這個判斷是
// core = true wc >= corePoolSize 核心線程到達上限 還要創(chuàng)建核心線程,不可以 返回false
// core = false wc >= maximumPoolSize 達到非核心(包含核心)線程上限 還要創(chuàng)建線程 返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//workerCount +1
if (compareAndIncrementWorkerCount(c))
//返回外層循環(huán) ,循環(huán)結(jié)束
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 {
// 創(chuàng)建 Worker包裝我們的任務(wù) firstTask
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());
// rs < SHUTDOWN 表示是RUNNING 狀態(tài)
// (rs == SHUTDOWN && firstTask == null) 表示SHUTDOWN狀態(tài) 且沒有不再執(zhí)行新的任務(wù)
//2種情況都會繼續(xù)執(zhí)行
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//上面2中情況 都會加入workers集合
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
// 設(shè)置 workerAdded = true;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//workerAdded = true; 上面設(shè)置過了
if (workerAdded) {
// 啟動線程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
//賦值 firstTask 并用getThreadFactory()得到我們創(chuàng)建時傳入的線程工廠調(diào)用newThread(this);創(chuàng)建線程,并將自己作為Runnable傳入
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
}
Worker是個Runnable,上面的w = new Worker(firstTask);
方法其實就是對任務(wù)的包裝 在構(gòu)造方法中賦值 firstTask 并用getThreadFactory()得到我們傳入的線程工廠調(diào)用newThread(this);創(chuàng)建線程,并將自己作為Runnable傳入。當(dāng)addWorker()
方法調(diào)用 t.start()
就會執(zhí)行Worker類中的run()
方法。
Worker類
/** Delegates main run loop to outer runWorker. */
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
//賦值firstTask給task
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//要執(zhí)行的任務(wù)不為空直接執(zhí)行,如果等于空調(diào)用getTask()去等待隊列取出任務(wù)
//對應(yīng)了最開始 (workerCountOf(recheck) == 0) addWorker(null, false); 這段判斷條件
//之前提過 添加空任務(wù)的目的就是不再執(zhí)行新任務(wù),去執(zhí)行等待隊列的任務(wù)。因此getTask()得以執(zhí)行
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 {
//執(zhí)行任務(wù)前調(diào)用此時已經(jīng)在線程中運行 ,因為這是在Worker中的run方法中
beforeExecute(wt, task);
Throwable thrown = null;
try {
//執(zhí)行我們傳入的Runnable
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 {
//執(zhí)行任務(wù)后調(diào)用
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
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);
// Are workers subject to culling?
//allowCoreThreadTimeOut 表示非核心線程的超時時間keepAliveTime是否作用于核心線程,默認(rèn)是false
// 所以只看后面的判斷。 wc > corePoolSize;表示大于核心線程 true
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//wc > corePoolSize;表示大于核心線程 true 那么就是創(chuàng)建非核心線程,非核心線程取等待隊列的任務(wù)有超時時間
//keepAliveTime時間內(nèi)阻塞。取不到 繼續(xù)向下執(zhí)行 任務(wù)完成 非核心線程執(zhí)行完銷毀
// wc <= corePoolSize; 小于或等于核心線程 workQueue.take();一直阻塞,當(dāng)隊列有任務(wù)時立馬執(zhí)行,這也是為什么核心線程一直存在的原因
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
總結(jié):
- 核心線程一直存在的原因的是
workQueue.take();
,非核心線程keepAliveTime時間后銷毀的原因是workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
- 從以下代碼可以看出。線程池中的任務(wù)達到corePoolSize線程數(shù)時,所有任務(wù)都會先添加到隊列,當(dāng)調(diào)用
workQueue.offer(command)
時,被workQueue.take();
阻塞的核心線程就會拿到任務(wù),然后去執(zhí)行,這很關(guān)鍵 - if (isRunning(c) && workQueue.offer(command)) 里的條件當(dāng)都都不滿足時,什么都不會做,之后會往隊列中添加元素。當(dāng)核心線程有空間是getTask中take()阻塞的核心線程會立馬取隊列取出個任務(wù)執(zhí)行
-
beforeExecute(wt, task);
和afterExecute(task, thrown);
會在任務(wù)執(zhí)行前后執(zhí)行 - 核心線程與非核心線程沒有本質(zhì)上的區(qū)別。只是在獲取任務(wù)時,通過此判斷
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
,非核心超時會結(jié)束,而非核心則會take()
阻塞住
int c = ctl.get();
int num=workerCountOf(c);
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);
通過源碼 得出前面提到的結(jié)論
實戰(zhàn)
根據(jù)學(xué)習(xí)到的源碼,封裝自己的線程池工具類,它有一下幾個特點:
/**
* 1. 支持按任務(wù)的優(yōu)先級去執(zhí)行,
* 2. 支持線程池暫停.恢復(fù)(批量文件下載,上傳) ,
* 3. 異步結(jié)果主動回調(diào)主線程
* todo 線程池能力監(jiān)控,耗時任務(wù)檢測,定時,延遲,
*
* 根據(jù)源碼分析 :當(dāng)工作線程小于核心線程是不會進入隊列
* 當(dāng)核心線程啟動后 每次都會進入隊列
*/
object MyExecutor {
private var hiExecutor: ThreadPoolExecutor
//暫停線程表示:hint:這里為啥不用volatile修飾?
private var isPause: Boolean = false
private val lock: ReentrantLock = ReentrantLock()
private val condition: Condition = lock.newCondition()
//主線程回調(diào)能力的Handler
private val handler: Handler = Handler(Looper.getMainLooper())
private val seq = AtomicLong()
/**
* 初始化構(gòu)建參數(shù)
*/
init {
//獲取cpu數(shù)
val cpuCount = Runtime.getRuntime().availableProcessors()
//核心線程數(shù)為 cpu+1
val corePoolSize = cpuCount + 1
//非核心線程數(shù)
val maxPoolSize = corePoolSize * 2 + 1
//非核心線程超時銷毀時間
val keepAliveTime = 30L
//超時時間單位 s
val unit = TimeUnit.SECONDS
//相當(dāng)于默認(rèn)實現(xiàn) 創(chuàng)建一個線程
val threadFactory = ThreadFactory() {
val thread = Thread(it)
//hi-executor-0
thread.name = "hi-executor-" + seq.getAndIncrement()
return@ThreadFactory Thread(it)
}
val priorityBlockingQueue: PriorityBlockingQueue<out Runnable> = PriorityBlockingQueue()
hiExecutor = object : ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
unit,
priorityBlockingQueue as BlockingQueue<Runnable>,
threadFactory
) {
override fun beforeExecute(t: Thread?, r: Runnable?) {
priorityBlockingQueue.size
if (isPause) {
try {
//條件喚醒線程標(biāo)準(zhǔn)寫法
lock.lock()
condition.await()
} finally {
lock.unlock()
}
}
}
override fun afterExecute(r: Runnable?, t: Throwable?) {
//監(jiān)控線程池耗時任務(wù),線程創(chuàng)建數(shù)量,正在運行的數(shù)量
HiLog.e("已執(zhí)行完的任務(wù)的優(yōu)先級是:" + (r as PriorityRunnable).priority.toString() + Thread.currentThread().name)
priorityBlockingQueue.size
}
}
}
/**
* priority越小 優(yōu)先級越高
* @IntRange 優(yōu)先級范圍
* @JvmOverloads 重載方法 生成多種重載方法
* 無返回結(jié)果 與正常線程池一樣
*/
@JvmOverloads
fun execute(@IntRange(from = 0, to = 10) priority: Int = 0, runnable: Runnable) {
hiExecutor.execute(PriorityRunnable(priority, runnable))
}
@JvmOverloads
fun execute(@IntRange(from = 0, to = 10) priority: Int = 0, callable: Callable<*>) {
val newFixedThreadPool = Executors.newCachedThreadPool()
hiExecutor.execute(PriorityRunnable(priority, callable))
}
/**
* 提供任務(wù)執(zhí)行后返回結(jié)果
* 并回調(diào)主線程的能力
*
* <T> 返回結(jié)果類型
*/
abstract class Callable<T> : Runnable {
override fun run() {
handler.post {
onPrepare()
}
//真正在線程中執(zhí)行
val t = onBackground()
handler.post {
onCompleted(t)
}
}
/**
* UI線程執(zhí)行
*/
open fun onPrepare() {
//預(yù)處理 加載動畫
}
/**
* 線程池中執(zhí)行
*/
abstract fun onBackground(): T
/**
* UI線程執(zhí)行
* 執(zhí)行完成
*/
abstract fun onCompleted(t: T)
}
/**
* 重寫的意義在于 PriorityBlockingQueue 這個具有排序的堆結(jié)構(gòu) 每個元素要有比較大小的能力
* priority 線程優(yōu)先級 runnable執(zhí)行任務(wù)的
*/
class PriorityRunnable(val priority: Int, private val runnable: Runnable) : Runnable,
Comparable<PriorityRunnable> {
override fun run() {
runnable.run()
}
override fun compareTo(other: PriorityRunnable): Int {
//倒序 priority越小 優(yōu)先級越高
return if (this.priority < other.priority) 1 else if (this.priority > other.priority) -1 else 0
}
}
//可能多個線程調(diào)用此方法
@Synchronized
fun pause() {
isPause = true
HiLog.e("hiexecutor is paused")
}
@Synchronized
fun resume() {
isPause = false
lock.lock()
try {
condition.signalAll()
} finally {
lock.unlock()
}
HiLog.e("hiexecutor is resume")
}
}
測試代碼:
private void priorityExecutor() {
for (int priority = 0; priority < 10; priority++) {
int finalPriority = priority;
HiExecutor.INSTANCE.execute(priority, () -> {
try {
Thread.sleep(1000 - finalPriority * 100);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
}
利用源碼分析后,我們可以起送封裝自己的線程池,它具有以下幾種能力:
- 支持按任務(wù)的優(yōu)先級去執(zhí)行,
- 支持線程池暫停.恢復(fù)(批量文件下載,上傳) ,
- 異步結(jié)果主動回調(diào)主線程
- 線程池能力監(jiān)控,耗時任務(wù)檢測,定時,延遲,
但執(zhí)行中有2個問題:
- 明明是使用優(yōu)先隊列,但為什么第一次的時候,任務(wù)優(yōu)先級卻是無序的,第二次就好了?
- 使用pause()方法,線程還在執(zhí)行?
對于問題1。
首先線程池的調(diào)度是,系統(tǒng)Cpu利用時間碎片隨機去調(diào)度的。我們的設(shè)置的只能盡可能的去滿足,按照優(yōu)先級去執(zhí)行,但不能保證。更重要的是。通過源碼得知。當(dāng)線程池中,核心線程數(shù)未到最大值時(測試?yán)又惺?strong>5),是不會加入到隊列的,因此也就不會排序。當(dāng)?shù)诙螆?zhí)行任務(wù),線程池就會先加入隊列。然后被take()阻塞的隊列取到任務(wù),然后執(zhí)行,此時就有了優(yōu)先級。 解決:可以在應(yīng)用啟動白屏頁啟動線程。異步去初始化第三方庫,或其他不需要優(yōu)先級的任務(wù)。這樣之后的任務(wù)就都具有優(yōu)先級了
對于問題2:
通過源碼分析:
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);
}
}
我們重寫的方法
override fun beforeExecute(t: Thread?, r: Runnable?) {
priorityBlockingQueue.size
if (isPause) {
try {
//條件喚醒線程標(biāo)準(zhǔn)寫法
lock.lock()
condition.await()
} finally {
lock.unlock()
}
}
}
beforeExecute(wt, task);
中的方法,只能阻塞當(dāng)前線程,和后續(xù)要執(zhí)行的線程,已經(jīng)在線程中開始執(zhí)行的任務(wù)是無法暫停的。 這其實不是問題。只是種誤解
總結(jié)
通過學(xué)習(xí)源碼,我們的收獲:
- 學(xué)習(xí)了int字段的多重利用,試想下,如果不采用源碼中的方式,我們至少要維護這幾種字段:
sunStatus
,runningWorkCount
,maxWorkCount
,shutDownWorkCount
,還要保證他們的線程安全。這是種值得借鑒的優(yōu)秀思想。ARouter中也用到類似思想(extars) - 清楚線程池的狀態(tài)和工作流程,理解線程池如何復(fù)用與非核心線程的銷毀
- 找到插手線程池的思路,擴展自己的線程池
- 通過學(xué)習(xí)源碼,還解決了2個看似不合理,但卻在源碼中有答案的問題。