上篇將android多線程篇幅過長,所以決定線程池這一張拆開來講。其實在我的okhttp源碼理解內(nèi)已經(jīng)談過線程池的使用,這邊我們稍微詳細談談線程池。一般我們使用線程池都會用Executors去獲得相應的線程池對象,而線程池總共有4種
- newCachedThreadPool 創(chuàng)建一個可緩存線程池了袁,如果線程池長度超過處理需要,可靈活回收空閑線程湿颅,若無可回收载绿,則新建線程,亦即無線擴增線程池油航。在線程空閑60秒后終止線程崭庸。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueue<Runnable>());
}```
* newFixedThreadPool 創(chuàng)建一個定長線程池,可控制線程最大并發(fā)數(shù)谊囚,超出的線程會在隊列中等待怕享。
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>(),threadFactory);
}```
- newScheduledThreadPool 繼承ThreadPoolExecutor,創(chuàng)建一個定長線程池镰踏,支持定時及周期性任務執(zhí)行函筋。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}```
* newSingleThreadExecutor 創(chuàng)建一個單線程化的線程池,它只會用唯一的工作線程來執(zhí)行任務奠伪,保證所有任務按照指定順序(FIFO, LIFO, 優(yōu)先級)執(zhí)行跌帐。
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(),threadFactory));
}```
顯然首懈,上面4種線程池都直接或者間接的來自于ThreadPoolExecutor,Executors只是提供了一個抽象工廠的功能去申請一個用戶需要的線程池,那么我們就來看看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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}```
* corePoolSize 核心線程數(shù)
* maximumPoolSize 線程池最大線程數(shù)究履,它表示在線程池中最多能創(chuàng)建多少個線程;
* keepAliveTime 表示線程沒有任務執(zhí)行時最多保持多久時間會終止佣盒。(默認情況下挎袜,只有當線程池中的線程數(shù)大于corePoolSize時顽聂,keepAliveTime才會起作用肥惭,直到線程池中的線程數(shù)不大于corePoolSize,即當線程池中的線程數(shù)大于corePoolSize時紊搪,如果一個線程空閑的時間達到keepAliveTime蜜葱,則會終止,直到線程池中的線程數(shù)不超過corePoolSize耀石。但是如果調(diào)用了allowCoreThreadTimeOut(boolean)方法牵囤,在線程池中的線程數(shù)不大于corePoolSize時,keepAliveTime參數(shù)也會起作用滞伟,直到線程池中的線程數(shù)為0揭鳞;)
* workQueue 一個阻塞隊列,用來存儲等待執(zhí)行的任務梆奈,這個參數(shù)的選擇也很重要野崇,會對線程池的運行過程產(chǎn)生重大影響,一般來說亩钟,這里的阻塞隊列有幾種選擇,常用的有1. LinkedBlockingQueue(此隊列按 FIFO(先進先出)排序元素) 2. SynchronousQueue(一個緩存值為1的阻塞隊列)有興趣的可以了解其他的blockQueue
* threadFactory 一個接口用來生成一個Thread
* handler: RejectedExecutionHandler類型表示當拒絕處理任務時的策略乓梨,有以下四種取值:
![handler策略.png](http://upload-images.jianshu.io/upload_images/1868403-724785fed399d79f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
然后看看它的父類AbstractExecutorService,這是一個抽象類
![AbstractExecutorService結構圖.png](http://upload-images.jianshu.io/upload_images/1868403-6b615cad82eb9d04.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
它實現(xiàn)了ExecutorService接口清酥,對應貼出此接口的結構圖
![ExecutorService結構圖.png](http://upload-images.jianshu.io/upload_images/1868403-642f012f3cd89b1f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
ExecutorService也是繼承了Executor扶镀,Executor只有一個void execute(Runnable command)方法 就不貼了
清晰了ThreadPoolExecutor的繼承關系后 我們回過頭來看看ThreadPoolExecutor
######在此之前我們看看線程的狀態(tài)
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
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;
ctl是一個線程安全的自增,自減數(shù)據(jù)結構焰轻,記錄
* RUNNING :Accept new tasks and process queued tasks線程池正常運行狀態(tài)
* SHUTDOWN :Don't accept new tasks, but process queued tasks調(diào)用shutDown()臭觉,進入此狀態(tài),不接受新的任務辱志,等待正在執(zhí)行的任務完成
* STOP :Don't accept new tasks, don't process queued tasks,and interrupt in-progress tasks調(diào)用shutdownNow()胧谈,不接受新任務,并主動嘗試去結束正在執(zhí)行的任務
* TIDYING :All tasks have terminated, workerCount is zero,the thread transitioning to state TIDYING will run the terminated() hook method 線程池為空荸频,就會到達這個狀態(tài)菱肖,執(zhí)行terminated()方法
* TERMINATED terminated()執(zhí)行完畢,所有任務結束后即進入此狀態(tài)
#####接著我們看看幾個主要的方法
* void execute(Runnable command) 執(zhí)行任務
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);
這里需要吐槽一下旭从,用了位運算后雖然性能提升了稳强,可是可讀性3≈佟!退疫!不說臟話渠缕,還得我去算。
![execute流程.png](http://upload-images.jianshu.io/upload_images/1868403-66348c50eb4ff5c3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
有點亂褒繁,呵呵呵亦鳞,大概講解下。首先棒坏,判斷當前工作隊列是否有空閑燕差,嘗試加入工作隊列,如果成功則結束坝冕,否則繼續(xù)下面判斷徒探,判斷線程池是否為running狀態(tài),并將任務加入到阻塞隊列喂窟,如果這兩個條件有一個不符合测暗,則擴容線程池不大于最大限制情況下嘗試加入,未成功的話磨澡,拋出拒絕句柄碗啄,若兩個條件符合,重新檢測線程池狀態(tài)稳摄,如果線程池不再running狀態(tài)則嘗試移除該任務稚字,并拋出拒絕句柄,否則判斷當前工作線程數(shù)是否為0秩命,暫停任務(這邊addWorker(null,false)不太理解尉共,請高手指出)
* submit() 執(zhí)行任務并返回結果,在AbstractExecutorService已實現(xiàn)將Runnable用RunnableFuture包裹弃锐,達到返回線程執(zhí)行結果的目的
* shutdown() 不接受新線程袄友,等待其他線程執(zhí)行完畢
* shutdownNow() 強制關閉所有線程
----
上面講了那么多,接下來我們回到原點霹菊,來看看ThreadPoolExecutor怎樣實現(xiàn)4個線程池的
* newCachedThreadPool初始化參數(shù)里BlockQueue這個結構是實現(xiàn)緩存最重要的結構剧蚣,由于newCachedThreadPool使用的是SynchronousQueue<Runnable>而這個隊列的特點就是每次只會保留一個在隊列內(nèi),這也保證了線程之間一定的隔離作用旋廷,還有corePoolSize為0鸠按,這就導致這樣一種場景,有任務來我就不斷的夸大線程池饶碘,任務完成后因為當前的size肯定大于corePoolSize所以就不斷銷毀線程
* newFixedThreadPool 這就是固定了corePoolSize目尖,maximumPoolSize不會額外申請新工作線程,LinkedBlockingQueue<Runnable>遵循FIFO排隊策略扎运,這是一個按排隊策略瑟曲,擁有固定吞吐量的線程池
* newScheduledThreadPool 這個是單獨寫了一個類饮戳,相對較復雜,BlockQueue用的是DelayedWorkQueue()主要看看ScheduledThreadPoolExecutor的調(diào)度策略
public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);
public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit);
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit);
在任務進入DelayedWorkQueue時會附帶時間參數(shù)洞拨,只有滿足了時間點扯罐,該任務才會進入工作線程,從而實現(xiàn)Schedule烦衣,纖細請參看[newScheduledThreadPool分析](https://segmentfault.com/a/1190000000395007)
* newSingleThreadExecutor 由于corePoolSize與maximumPoolSize都為1歹河,且BlockQueue為LinkedBlockingQueue所以線程池里永遠只會有一個線程且支持FIFO策略