0. 相關資料
源碼地址
android.os.AsyncTask
java.util.concurrent.Executors
參考文章
Android實戰技巧:深入解析AsyncTask
ThreadPoolExecutor Java文檔
Executors Java文檔
java中關鍵字volatile的作用
1. AsyncTask#execute與AsyncTask#executeOnExecutor
public static void execute(Runnable runnable)
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params)
AsyncTask#execute本質是調用THREAD_POOL_EXECUTOR,通過synchronized來保證線程池中的任務是一個一個調用的。
AsyncTask#executeOnExecutor可以指定一個Execurtor來進行任務調度。
AsyncTask擁有2個內置Executor,Executors擁有4個內置的Executor,這6個Executor都可以在AsyncTask#executeOnExecutor上面使用。一個Executor最多能執行多少個任務?這些Executor有什麼區別?如何自定義一個Executor?這些問題是本文關注的重點。
2. ThreadPoolExecutor的構造
上面說的六個Executor都是ThreadPoolExecutor,區別是構造對象的時候,使用的參數不同。下面說明ThreadPoolExecutor的構造函數。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor Java文檔的描述如下:
corePoolSize - 池中所保存的線程數,包括空閒線程。
maximumPoolSize - 池中允許的最大線程數。
keepAliveTime - 當線程數大於核心時,此爲終止前多餘的空閒線程等待新任務的最長時間。
unit - keepAliveTime 參數的時間單位。
workQueue - 執行前用於保持任務的隊列。此隊列僅保持由 execute 方法提交的 Runnable 任務。
另外文檔中描述了corePoolSize與maximumPoolSize的關係:如果運行的線程多於 corePoolSize 而少於 maximumPoolSize,則僅當隊列滿時才創建新線程。
我的理解是這樣的:
如果運行的任務數量小於corePoolSize ,那麼新的任務會立即執行。
如果任務數量大於了corePoolSize ,那麼會放到workQueue中,如果workQueue滿了,但是任務數量沒有超過maximumPoolSize ,那麼創建一個新線程來執行。
如果workQueue滿了,池中任務數量已經達到maximumPoolSize,那麼會執行ThreadPoolExecutor的錯誤處理機制,默認是丟棄該任務。
3. AsyncTask內置Executor
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
SERIAL_EXECUTOR實際上調用THREAD_POOL_EXECUTOR,只是通過synchronized來保證線程池中的任務是一個一個調用的。AsyncTask#execute就是使用SERIAL_EXECUTOR,因此AsyncTask#execute在執行的時候是一個一個執行的。注意SERIAL_EXECUTOR 是靜態的,所以無論在哪個線程中,使用AsyncTask#execute執行任務,任務都在一起執行。
THREAD_POOL_EXECUTOR,corePoolSize=5,maximumPoolSize=128,keepAliveTime =1,keepAliveTime =秒,workQueue= LinkedBlockingQueue<Runnable>(10)
可以有5個線程可以同時運行
超過5個之後的線程會放到workQueue
隊列中最多放10個,超過之後,直接創建新的線程直接執行
最多一共能放128個線程,超過128個之後的線程會被丟棄
4. Executors的內置Executor
Executors#newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
SynchronousQueue是不存儲元素的阻塞隊列。由於corePoolSize=0,每次新來一個任務,都會放到workQueue中,但是SynchronousQueue是不存儲元素的,所以會立即開啓線程執行。由於maximumPoolSize=Integer.MAX_VALUE,執行的線程數量是沒有上限的。文檔中說,對於執行很多短期異步任務的程序而言,這些線程池通常可提高程序性能。
Executors#newFixedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
池中最多可以同時運行nThreads個線程,多餘的任務都放在workQueue中,workQueue沒有上限,可以存儲任意多個任務。
Executors#newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
池中只能運行一個線程,其他任務都放在workQueue中。相當於Executors#newFixedThreadPool(1)。
Executors#newSingleThreadScheduledExecutor
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
創建一個單線程執行程序,它可安排在給定延遲後運行命令或者定期地執行。
這裏主要關注兩個Executor,Executors#newCachedThreadPool()和Executors#newFixedThreadPool()。