Android AsyncTask兩種線程池分析和總結

本文轉載自:http://bbs.51cto.com/thread-1114378-1.html
(一)    前言
在android AsyncTask裏面有兩種線程池供我們調用
1.    THREAD_POOL_EXECUTOR, 異步線程池
2.    SERIAL_EXECUTOR,同步線程池
正如上面名稱描述的那樣,一個是異步線程池,多個任務在線程池中併發執行;還有一個是同步執行的。
默認的話,直接調用execute的話,是使用SERIAL_EXECUTOR
下面的話,會用源代碼的方式來說明這兩種線程池的作用和注意事項。
(二)     THREAD_POOL_EXECUTOR用法舉例
1.    代碼

2.    使用方法比較簡單,首先創建一個繼承自AsyncTask的MyAsyncTask類,然後調用

MyAsyncTask asynct = newMyAsyncTask(task);
asynct.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,0);

就可以了。
3.    上面代碼執行的時候會出錯,導致程序異常終止,如下圖
 
原因是:


就是因爲我們嘗試添加500個task到AsyncTask.THREAD_POOL_EXECUTOR線程池中,但是它的核心線程是5,隊列容量是128,最大線程數是9。
所以,拋出了這個異常。
那麼,接下來的話,我們會去分析這個異常怎麼出來的。

(三)     THREAD_POOL_EXECUTOR代碼分析
從AsyncTask.THREAD_POOL_EXECUTOR的定義開始分析
1.    代碼路徑
frameworks\base\core\java\android\os\AsyncTask.java

代碼:

privatestaticfinal int CPU_COUNT = Runtime.getRuntime().availableProcessors();
privatestaticfinal int CORE_POOL_SIZE = CPU_COUNT + 1;
privatestaticfinal int MAXIMUM_POOL_SIZE = CPU_COUNT * 2+1;
privatestaticfinal int KEEP_ALIVE = 1;
....
/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
 publicstaticfinal Executor THREAD_POOL_EXECUTOR = newThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                 TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

那麼,繼續往下面看,看這幾個參數傳進去後是什麼意思。

2.    代碼路徑
\libcore\luni\src\main\java\java\util\concurrent\ThreadPoolExecutor.java

代碼:


這是ThreadPoolExecutor的構造函數,首先需要明白的是這幾個參數的含義
A.    corePoolSize: 線程池維護線程的最少數量
B.    maximumPoolSize:線程池維護線程的最大數量
C.    keepAliveTime: 線程池維護線程所允許的空閒時間
D.    unit: 線程池維護線程所允許的空閒時間的單位
E.    workQueue: 線程池所使用的緩衝隊列
F.    handler: 線程池對拒絕任務的處理策略

當一個任務通過asynct.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0)方法欲添加到線程池時:
如果此時線程池中的數量小於corePoolSize,即使線程池中的線程都處於空閒狀態,也要創建新的線程來處理被添加的任務。
如果此時線程池中的數量等於 corePoolSize,但是緩衝隊列 workQueue未滿,那麼任務被放入緩衝隊列。
如果此時線程池中的數量大於corePoolSize,緩衝隊列workQueue滿,並且線程池中的數量小於maximumPoolSize,建新的線程來處理被添加的任務。
如果此時線程池中的數量大於corePoolSize,緩衝隊列workQueue滿,並且線程池中的數量等於maximumPoolSize,那麼通過 handler所指定的策略來處理此任務。

也就是:處理任務的優先級爲:
核心線程corePoolSize、任務隊列workQueue、最大線程maximumPoolSize,如果三者都滿了,使用handler處理被拒絕的任務。

當線程池中的線程數量大於 corePoolSize時,如果某線程空閒時間超過keepAliveTime,線程將被終止。這樣,線程池可以動態的調整池中的線程數。

unit可選的參數爲java.util.concurrent.TimeUnit中的幾個靜態屬性:
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。

workQueue是BlockQueue的子類,ArrayBlockingQueue,DelayQueue

handler有四個選擇(這不是android的Handler):
ThreadPoolExecutor.AbortPolicy() – 這個也是AsyncTask.THREAD_POOL_EXECUTOR使用的
拋出java.util.concurrent.RejectedExecutionException異常
ThreadPoolExecutor.CallerRunsPolicy()
重試添加當前的任務,他會自動重複調用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy()
拋棄舊的任務
ThreadPoolExecutor.DiscardPolicy()
拋棄當前的任務

所以,正是我們的AsyncTask.THREAD_POOL_EXECUTOR使用了AbortPolicy()類型的handler,所以纔會拋出異常..

那麼,在把任務添加到AsyncTask.THREAD_POOL_EXECUTOR之後,下面的工作就是由這個線程池來調度線程執行任務了。

(四)     AsyncTask. SERIAL_EXECUTOR
1.    使用方法
AsyncTask. SERIAL_EXECUTOR的使用方法和Async.THREAD_POOL_EXECUTOR差不多。不過正如前面所說,它是默認的Executor,所以可以直接調用,所以可以有兩種調用方法。

a.    asynct.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, 0);

b.    asynct.execute(0);
效果是一樣的

2.執行流程
代碼路徑:
frameworks\base\core\java\android\os\AsyncTask.java

代碼:


嗯,它會調用到SerialExecutor.execute(Runnable r)方法
在這個方法裏面,它首先把任務放到mTasks這個集合裏面;然後判斷mActivie是否爲空,再調用scheduleNext ()方法。
mActivie爲null的意思是當前沒有任務在執行,如果mActivie!=null,那麼說明當前有任務正在執行,那麼只要把任務添加到mTasks裏面即可。
因爲任務執行完畢後,會再次調用scheduleNext()方法的,就是
finally {

  scheduleNext();

}

這樣就形成了一種鏈狀調用結構,只要mTasks裏面還有任務,就會不斷逐一調用,如果後面有任務進來,就只要添加到mTasks裏面即可。
同時,不知道大家注意到沒有,這兩個方法都是synchronized的,這樣,就保證了多線程之間調度問題。
否則肯定會出現問題的,至於什麼問題,大家想想就能明白。

4.    繼續分析scheduleNext()方法
這個方法首先把mTasks裏面的數據取一個出來,然後調用
THREAD_POOL_EXECUTOR.execute(mActive);
我暈,這不就是上面一直在分析的AsyncTask.THREAD_POOL_EXECUTOR麼?
好吧,原來AsyncTask.THREAD_POOL_EXECUTOR和AsyncTask.SERIAL_EXECUTOR的區別就是SERIAL_EXECUTOR在THREAD_POOL_EXECUTOR的基礎上添加了一個mTasks的集合來保證任務順序執行而已...

(五)     總結
說了這麼多,總結下
1.    AsyncTask裏面有THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR兩種方式來異步執行任務;THREAD_POOL_EXECUTOR是異步的,而SERIAL_EXECUTOR任務是順序執行的。
2.    THREAD_POOL_EXECUTOR如果添加的任務過多,沒有及時處理的話,會導致程序崩潰,它的隊列size是128;它的調度規則是核心池大小,隊列大小,以及最大線程數和異常處理Handler來決定的。
3.    SERIAL_EXECUTOR本質是在THREAD_POOL_EXECUTOR的基礎上添加一個mTasks的集合來保證任務的順序執行。

參考網址
http://blog.sina.com.cn/s/blog_8417aea80100t483.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章