線程池主要相關的有如下幾個類:
1、Executor
最底層的接口,規定了一個void execute(Runnable command)接口,用於執行線程任務;
2、ExecutorService、AbstractExecutorService
ExecutorService抽象類繼承Executor接口,AbstractExecutorService抽象類繼承ExecutorService。這兩個抽象類對線程池做了進一步封裝;
3、ThreadPoolExecutor
ThreadPoolExecutor是就是線程池最終的實現了,繼承自AbstractExecutorService抽象類;
4、Executors
Executors是一個快速創建ThreadPoolExecutor的工具類。提供了多個靜態方法用於創建各種類型的線程池,其本質也是調用new ThreadPoolExecutor()方法創建的;
線程池的主要優點有三個:
1、重用線程,提高性能。創建、銷燬線程都是很耗資源的;
2、控制線程數量,避免大量線程搶佔資源而導致的阻塞現象;
3、能夠對線程進行簡單的管理。比如定時執行、排隊執行等;
ThreadPoolExecutor
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;
}
1、參數 int corePoolSize
線程池的核心線程數量。核心線程會一直存活,即使它們處於空閒狀態。當然,如果將allowCoreThreadTimeOut屬性設置爲true,當核心線程空閒時間超過一定時間(由keepAliveTime屬性指定)後,還是會終止;
2、參數 int maximumPoolSize
線程池所能容納的最大線程數。當活動線程數達到極限後,新加入的線程會被阻塞;
3、參數 long keepAliveTime
線程空閒超時時間。當線程空閒時間超過這個值後,就會被系統終止;
4、參數 TimeUnit unit
用於指定keepAliveTime的時間單位。這是一個枚舉值,常用的有毫秒MILLISECONDS、秒SECONDS、分鐘MINUTES;
5、參數 BlockingQueue workQueue
存儲任務的隊列,就是存儲了Runnable對象的集合。既然可以添加/存儲很多Runnable對象,爲什麼還說線程數量是指定的呢?因爲這個集合只是存儲了Runnable對象,並不會調用其start()方法啓動線程來執行任務,而是在指定的線程中調用Runnable對象的run()方法,執行run()方法裏面的邏輯而已。
6、參數 ThreadFactory threadFactory
線程工廠,爲線程池提供創建線程的功能。ThreadFactory是一個接口,它只聲明瞭一個方法Thread newThread(Runnable r);
另外,ThreadPoolExecutor中還有個RejectedExecutionHandler handler成員變量,通過setRejectedExecutionHandler()方法設置。當線程池無法執行新任務時(比如線程已滿),會通過這個handler通知調用處。
線程池執行任務時,會遵循如下規則:
1、如果線程數未達到corePoolSize,當有新任務插入的時候,會啓動一個核心線程進行執行;
2、如果線程數已經達到corePoolSize,當有新任務插入時,會插入任務隊列中排隊等待;
3、如果任務隊列也滿了,就啓動一個非核心線程執行該任務;
4、如果線程數已經達到maximumPoolSize,就拒絕該任務,並通過RejectedExecutionHandler通知調用處;
Executors
一般情況下,我們很少會自己new ThreadPoolExecutor,而是使用Executors工具類來快速創建所需的線程池,我們看一下Executors的幾個常用方法:
1、固定線程數線程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
可以看出,這個線程池核心線程數和最大線程數一樣,所以這個線程池只有核心線程數,並且沒有超時時間,即使線程空閒,也不會終止。由於這些線程都不會被回收,所以這樣的線程池能快速響應外界請求。
2、單線程線程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
從構造方法的參數可以看出,這是一個單線程的線程池,裏面只有一個核心線程。它的意義在於,統一在一個線程中處理所有任務,不需要考慮線程間的同步問題。
3、緩存線程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
這個線程池沒有核心線程,全部是非核心線程,最大線程數是0x7fffffff,幾乎等於無限,並且設置了60秒的超時時間。這個線程池的任務隊列爲SynchronousQueue,這個比較特殊,可以理解爲它是一個無法存儲元素的隊列,也就是說這個線程池沒有任務隊列,即所有的新任務都會立即執行,要麼啓動新線程,要麼使用空閒線程。這種線程池比較適合量大、耗時少的任務,當所有任務執行完,超過60秒就回收,幾乎不佔用系統資源。
4、定時線程池
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue(), threadFactory);
}
這個線程池的核心線程固定,非核心線程無限大,超時時間DEFAULT_KEEPALIVE_MILLIS爲10毫秒,也就是空閒非核心線程立即回收。任務隊列是延時任務隊列,這類線程池主要是用來執行延時任務和重複任務的。
總結
一般情況下我們很少會自己來new這些線程池,但是SDK中很多地方都用到了線程池,上一篇研究了Handler,今天研究了線程池,學會了這兩個工具,我們就可以定製出更高效、更簡潔的異步任務處理方法了。
下一篇就以AsynTask源碼爲例子,來研究一下Handler和線程池在其中的運用。