0.前言
轉載請注明出處:Android開發——Android中常見的4種線程池
使用線程池可以給我們帶來很多好處,首先通過線程池中線程的重用,減少創建和銷毀線程的性能開銷。其次,能控制線程池中的並發數,否則會因為大量的線程爭奪CPU資源造成阻塞。最后,線程池能夠對線程進行管理,比如使用ScheduledThreadPool來設置延遲N秒后執行任務,並且每隔M秒循環執行一次。
下面會通過介紹線程池中的真正實現者——ThreadPoolExecutor來引出Android中的4類線程池的使用以及特性分析,會加上筆者自己的理解,和自認為比較恰當的比喻,幫助理解。
1.凡事得靠ThreadPoolExecutor(鋪墊環節,懂的直接跳過)
Executor作為一個接口,它的具體實現就是ThreadPoolExecutor。
Android中的線程池都是直接或間接通過配置ThreadPoolExecutor來實現不同特性的線程池。
先介紹ThreadPoolExecutor的一個常用的構造方法。
/*
*@ ThreadPoolExecutor構造參數介紹
*@author SEU_Calvin
* @date 2016/09/03
*/
public ThreadPoolExecutor(
//核心線程數,除非allowCoreThreadTimeOut被設置為true,否則它閑着也不會死
int corePoolSize,
//最大線程數,活動線程數量超過它,后續任務就會排隊
int maximumPoolSize,
//超時時長,作用於非核心線程(allowCoreThreadTimeOut被設置為true時也會同時作用於核心線程),閑置超時便被回收
long keepAliveTime,
//枚舉類型,設置keepAliveTime的單位,有TimeUnit.MILLISECONDS(ms)、TimeUnit. SECONDS(s)等
TimeUnit unit,
//緩沖任務隊列,線程池的execute方法會將Runnable對象存儲起來
BlockingQueue<Runnable> workQueue,
//線程工廠接口,只有一個new Thread(Runnable r)方法,可為線程池創建新線程
ThreadFactory threadFactory)
ThreadPoolExecutor的各個參數所代表的特性注釋中已經寫的很清楚了,那么ThreadPoolExecutor執行任務時的心路歷程是什么樣的呢?(以下用currentSize表示線程池中當前線程數量)
-
當currentSize<corePoolSize時,沒什么好說的,直接啟動一個核心線程並執行任務。
-
當currentSize>=corePoolSize、並且workQueue未滿時,添加進來的任務會被安排到workQueue中等待執行。
-
當workQueue已滿,但是currentSize<maximumPoolSize時,會立即開啟一個非核心線程來執行任務。
-
當currentSize>=corePoolSize、workQueue已滿、並且currentSize>maximumPoolSize時,調用handler默認拋出RejectExecutionExpection異常。
2. Android中的四類線程池
Android中最常見的四類具有不同特性的線程池分別為FixThreadPool、CachedThreadPool、ScheduleThreadPool以及SingleThreadExecutor。
2.1 FixThreadPool(一堆人排隊上公廁)
/*
*@FixThreadPool介紹
*@author SEU_Calvin
* @date 2016/09/03
*/
public static ExecutorService newFixThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
//使用
Executors.newFixThreadPool(5).execute(r);
-
從配置參數來看,FixThreadPool只有核心線程,並且數量固定的,也不會被回收,所有線程都活動時,因為隊列沒有限制大小,新任務會等待執行。
-
【前方高能,筆者腦洞】FixThreadPool其實就像一堆人排隊上公廁一樣,可以無數多人排隊,但是廁所位置就那么多,而且沒人上時,廁所也不會被拆遷,哈哈o(∩_∩)o ,很形象吧。
-
由於線程不會回收,FixThreadPool會更快地響應外界請求,這也很容易理解,就好像有人突然想上廁所,公廁不是現用現建的。
2.2 SingleThreadPool(公廁里只有一個坑位)
/*
*@SingleThreadPool介紹
*@author SEU_Calvin
* @date 2016/09/03
*/
public static ExecutorService newSingleThreadPool (int nThreads){
return new FinalizableDelegatedExecutorService ( new ThreadPoolExecutor (1, 1, 0, TimeUnit. MILLISECONDS, new LinkedBlockingQueue<Runnable>()) );
}
//使用
Executors.newSingleThreadPool ().execute(r);
-
從配置參數可以看出,SingleThreadPool只有一個核心線程,確保所有任務都在同一線程中按順序完成。因此不需要處理線程同步的問題。
-
【前方高能,筆者腦洞】可以把SingleThreadPool簡單的理解為FixThreadPool的參數被手動設置為1的情況,即Executors.newFixThreadPool(1).execute(r)。所以SingleThreadPool可以理解為公廁里只有一個坑位,先來先上。為什么只有一個坑位呢,因為這個公廁是收費的,收費的大爺上年紀了,只能管理一個坑位,多了就管不過來了(線程同步問題)。
2.3 CachedThreadPool(一堆人去一家很大的咖啡館喝咖啡)
/*
*@CachedThreadPool介紹
*@author SEU_Calvin
* @date 2016/09/03
*/
public static ExecutorService newCachedThreadPool(int nThreads){
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit. SECONDS, new SynchronousQueue<Runnable>());
}
//使用
Executors.newCachedThreadPool().execute(r);
-
CachedThreadPool只有非核心線程,最大線程數非常大,所有線程都活動時,會為新任務創建新線程,否則利用空閑線程(60s空閑時間,過了就會被回收,所以線程池中有0個線程的可能)處理任務。
-
任務隊列SynchronousQueue相當於一個空集合,導致任何任務都會被立即執行。
-
【前方高能,筆者腦洞】CachedThreadPool就像是一堆人去一個很大的咖啡館喝咖啡,里面服務員也很多,隨時去,隨時都可以喝到咖啡。但是為了響應國家的“光盤行動”,一個人喝剩下的咖啡會被保留60秒,供新來的客人使用,哈哈哈哈哈,好惡心啊。如果你運氣好,沒有剩下的咖啡,你會得到一杯新咖啡。但是以前客人剩下的咖啡超過60秒,就變質了,會被服務員回收掉。
-
比較適合執行大量的耗時較少的任務。喝咖啡人挺多的,喝的時間也不長。
2.4 ScheduledThreadPool(4個里面唯一一個有延遲執行和周期重復執行的線程池)
/*
*@ScheduledThreadPool介紹
*@author SEU_Calvin
* @date 2016/09/03
*/
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize){
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS, new DelayedQueue ());
}
//使用,延遲1秒執行,每隔2秒執行一次Runnable r
Executors. newScheduledThreadPool (5).scheduleAtFixedRate(r, 1000, 2000, TimeUnit.MILLISECONDS);
-
核心線程數固定,非核心線程(閑着沒活干會被立即回收)數沒有限制。
-
從上面代碼也可以看出,ScheduledThreadPool主要用於執行定時任務以及有固定周期的重復任務。
至此Android中最常見的四類不同特性的線程池內容總結完畢。