Android線程池

上篇將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策略
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末花吟,一起剝皮案震驚了整個濱河市秸歧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌示辈,老刑警劉巖矾麻,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異芭梯,居然都是意外死亡险耀,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進店門玖喘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甩牺,“玉大人,你說我怎么就攤上這事累奈”崤桑” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵澎媒,是天一觀的道長搞乏。 經(jīng)常有香客問我,道長戒努,這世上最難降的妖魔是什么请敦? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任储玫,我火速辦了婚禮,結果婚禮上匣椰,老公的妹妹穿的比我還像新娘。我一直安慰自己端礼,他們只是感情好禽笑,可當我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布弛车。 她就那樣靜靜地躺著蒲每,像睡著了一般。 火紅的嫁衣襯著肌膚如雪邀杏。 梳的紋絲不亂的頭發(fā)上贫奠,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天,我揣著相機與錄音望蜡,去河邊找鬼唤崭。 笑死脖律,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的小泉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼酸茴,長吁一口氣:“原來是場噩夢啊……” “哼兢交!你這毒婦竟也來了薪捍?” 一聲冷哼從身側響起配喳,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎被济,沒想到半個月后息拜,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡少欺,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年赞别,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片犹芹。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡鞠绰,死狀恐怖腰埂,靈堂內(nèi)的尸體忽然破棺而出蜈膨,到底是詐尸還是另有隱情翁巍,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布肝断,位于F島的核電站驰凛,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏洒嗤。R本人自食惡果不足惜魁亦,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一洁奈、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧利术,春花似錦、人聲如沸印叁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽跃洛。三九已至,卻和暖如春汇竭,著一層夾襖步出監(jiān)牢的瞬間穴张,已是汗流浹背两曼。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留叮贩,地道東北人佛析。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像寸莫,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子桃纯,可洞房花燭夜當晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容