阿里巴巴Android開發(fā)手冊對線程池使用的建議:
【推薦】ThreadPoolExecutor 設(shè)置線程存活時間(setKeepAliveTime),確??臻e時線程能被釋放。
【強制】線程池不允許使用Executors 去創(chuàng)建,而是通過ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規(guī)則,規(guī)避資源耗盡的風險。
說明:
Executors 返回的線程池對象的弊端如下:
FixedThreadPool 和SingleThreadPool :允許的請求隊列長度為
Integer.MAX_VALUE,可能會堆積大量的請求,從而導致OOM;
CachedThreadPool 和ScheduledThreadPool :允許的創(chuàng)建線程數(shù)量為
Integer.MAX_VALUE,可能會創(chuàng)建大量的線程,從而導致OOM。
正例:
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
int KEEP_ALIVE_TIME = 1;
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,
NUMBER_OF_CORES*2, KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT,
taskQueue, new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());
反例:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
大部分情況我們使用Executors工具類去構(gòu)建線程池,大概分為以下5種不同的線程池,如下
//構(gòu)建線程數(shù)為1的線程池,等價于 newFixedThreadPool(1) 所構(gòu)造出的線程池
Executors.newSingleThreadExecutor();
//構(gòu)建包含固定線程數(shù)的線程池,默認情況下,空閑線程不會被回收
Executors.newFixedThreadPool(1);
//構(gòu)建線程數(shù)不定的線程池,線程數(shù)量隨任務(wù)量變動,空閑線程存活時間超過60秒后會被回收
Executors.newCachedThreadPool();
//構(gòu)建核心線程數(shù)為 corePoolSize,可執(zhí)行定時任務(wù)的線程池
Executors.newScheduledThreadPool(1);
//等價于 newScheduledThreadPool(1)
Executors.newSingleThreadScheduledExecutor();
ThreadManager 工具類,創(chuàng)建ThreadToolExecutor需要7個參數(shù),如下

線程池-核心參數(shù).png
/**
* <p>
* 1.降低資源消耗
* 可以重復利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。
* 2.提高響應(yīng)速度
* 當任務(wù)到達時,任務(wù)可以不需要等到線程創(chuàng)建就能立即執(zhí)行。
* 3.提高線程的可管理性
* 線程是稀缺資源,如果無限制地創(chuàng)建,不僅會消耗系統(tǒng)資源,還會降低系統(tǒng)的穩(wěn)定性,使用線程池可以進行統(tǒng)一分配、調(diào)優(yōu)和監(jiān)控。
* <p>
* 最頂層的接口Executor僅聲明了一個方法execute。
* ExecutorService 接口在其父接口基礎(chǔ)上,聲明了包含但不限于shutdown、shutdownNow、submit、invokeAll等方法。
* ScheduledExecutorService接口,
* 則是聲明了一些和定時任務(wù)相關(guān)的方法:schedule、scheduleAtFixedRate等。
* 線程池的核心實現(xiàn)是在ThreadPoolExecutor類中,
* 我們使用Executors調(diào)用newFixedThreadPool、newSingleThreadExecutor和newCachedThreadPool
* 等方法創(chuàng)建線程池均是ThreadPoolExecutor類型。
*/
public class ThreadManager {
private static final String TAG = "ThreadManager";
/**
* 根據(jù)cup核心數(shù)設(shè)置線程池數(shù)量
* <p>
* 《Android開發(fā)藝術(shù)探索》一書中建議:
* 核心線程數(shù)等于CPU核心數(shù)+1;
*/
private static final int corePoolSize = Runtime.getRuntime().availableProcessors();
/**
* 最大線程池數(shù)量= cpu核心數(shù)*2+1
* <p>
* 《Android開發(fā)藝術(shù)探索》一書中建議:
* 線程池的最大線程數(shù)等于CPU的核心數(shù)的2倍+1;
*/
private static final int maximumPoolSize = corePoolSize * 2 + 1;
/**
* 等待線程的存活時間
* 核心線程無超時機制,分核心線程的閑置時間為4秒;
*/
private static final long keepAliveTime = 30;
/**
* 等待線程存活時間的單位
* <p>
* TimeUnit.MINUTES 代表六十秒的時間單位
*/
private static final TimeUnit unit = TimeUnit.MINUTES;
/**
* 3、 線程資源回收策略
* 考慮到系統(tǒng)資源是有限的,對于線程池超出 corePoolSize 數(shù)量的空閑線程應(yīng)進行回收操作。進行此操作存在一個問題,即回收時機。
* 目前的實現(xiàn)方式是當線程空閑時間超過 keepAliveTime 后,進行回收。除了核心線程數(shù)之外的線程可以進行回收,核心線程內(nèi)的空閑線程也可以進行回收。
* 回收的前提是allowCoreThreadTimeOut屬性被設(shè)置為 true,通過public void allowCoreThreadTimeOut(boolean) 方法可以設(shè)置屬性值。
* <p>
* 4、 排隊策略
* 如上面線程創(chuàng)建規(guī)則所說的,當線程數(shù)量大于等于corePoolSize,workQueue未滿時,則緩存新任務(wù)。
* 這里要考慮使用什么類型的容器緩存新任務(wù),通過 JDK 文檔介紹,
* 我們可知道有3種類型的容器可供使用,分別是同步隊列,有界隊列和無界隊列。對于有優(yōu)先級的任務(wù),這里還可以增加優(yōu)先級隊列。
* 以上所介紹的4種類型的隊列,對應(yīng)的實現(xiàn)類如下:
* <p>
* SynchronousQueue 同步隊列 該隊列不存儲元素,每個插入操作必須等待另一個線程調(diào)用移除操作,否則插入操作會一直阻塞
* ArrayBlockingQueue 有界隊列 基于數(shù)組的阻塞隊列,按照 FIFO 原則對元素進行排序
* LinkedBlockingQueue 無界隊列 基于鏈表的阻塞隊列,按照 FIFO 原則對元素進行排序
* PriorityBlockingQueue 優(yōu)先級隊列 具有優(yōu)先級的阻塞隊列
*/
private static final BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
/**
* 5、線程工廠??赏ㄟ^工廠為新建的線程設(shè)置更有意義的名字
*/
private static final ThreadFactory threadFactory = Executors.defaultThreadFactory();
/**
* 6、拒絕策略
* 當線程池和任務(wù)隊列均處于飽和狀態(tài)時,使用拒絕策略處理新任務(wù)。默認是 AbortPolicy,即直接拋出異常
* <p>
* 如上線程創(chuàng)建規(guī)則策略中所說,當線程數(shù)量大于等于 maximumPoolSize,且 workQueue 已滿,
* 或者是當前線程池被關(guān)閉了則使用拒絕策略處理新任務(wù)。Java 線程池提供了4種拒絕策略實現(xiàn)類,
* 如下:
* <p></p>
* AbortPolicy 丟棄新任務(wù),并拋出 RejectedExecutionException
* DiscardPolicy 不做任何操作,直接丟棄新任務(wù)
* DiscardOldestPolicy 丟棄隊列列首的元素,并執(zhí)行新任務(wù)
* CallerRunsPolicy 會在線程池當前正在運行的Thread線程池中處理被拒絕的任務(wù)
*/
private static final RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();
private static final ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
private ThreadManager() {
}
/**
* 餓漢單例緩存線程池
* 優(yōu)點:對象優(yōu)先創(chuàng)建,無須等待,效率高。
* 缺點:申明靜態(tài)對象的時候就已經(jīng)初始化,一定程度上造成了資源的浪費。
*/
private static final ThreadManager INSTANCE = new ThreadManager();
public static ThreadManager get() {
return INSTANCE;
}
/**
* submit在執(zhí)行過程中與execute不一樣,不會拋出異常而是把異常保存在成員變量中,
* 在FutureTask.get阻塞獲取的時候再把異常拋出來
*/
public void execute(Runnable runnable) {
executor.execute(runnable);
}
/**
* 通過Future可以很輕易地獲得任務(wù)的執(zhí)行情況,比如是否執(zhí)行完成、是否被取消、是否異常等等
*/
public Future<?> submit(Runnable runnable) {
return executor.submit(runnable);
}
/**
* 調(diào)用 shutdown 和 shutdownNow 方法關(guān)閉線程池后,就不能再向線程池提交新任務(wù)了。
* 對于處于關(guān)閉狀態(tài)的線程池,會使用拒絕策略處理新提交的任務(wù)。
* <p>
* shutdown 會將線程池的狀態(tài)設(shè)置為SHUTDOWN,同時該方法還會中斷空閑線程
*/
public void shutdown() {
executor.shutdown();
}
/**
* shutdownNow 則會將線程池狀態(tài)設(shè)置為STOP,并嘗試中斷所有的線程
*/
public void shutdownNow() {
executor.shutdownNow();
}
}
/**
* 餓漢單例緩存線程池
* 優(yōu)點:對象優(yōu)先創(chuàng)建,無須等待,效率高。
* 缺點:申明靜態(tài)對象的時候就已經(jīng)初始化,一定程度上造成了資源的浪費。
*/
public class ThreadUtils {
private static final ThreadUtils INSTANCE = new ThreadUtils();
private final ExecutorService threadPool = Executors.newCachedThreadPool();
private ThreadUtils() {
//構(gòu)建線程數(shù)為1的線程池,等價于 newFixedThreadPool(1) 所構(gòu)造出的線程池
Executors.newSingleThreadExecutor();
//構(gòu)建包含固定線程數(shù)的線程池,默認情況下,空閑線程不會被回收
Executors.newFixedThreadPool(1);
//構(gòu)建線程數(shù)不定的線程池,線程數(shù)量隨任務(wù)量變動,空閑線程存活時間超過60秒后會被回收
Executors.newCachedThreadPool();
//構(gòu)建核心線程數(shù)為 corePoolSize,可執(zhí)行定時任務(wù)的線程池
Executors.newScheduledThreadPool(1);
//等價于 newScheduledThreadPool(1)
Executors.newSingleThreadScheduledExecutor();
}
public static ThreadUtils get() {
return INSTANCE;
}
public Future<?> submit(Runnable runnable) {
if (runnable != null) {
return threadPool.submit(runnable);
}
return null;
}
}