當前位置:
首頁 >
Android之AsyncTask两种线程池分析和总结
發布時間:2023/12/4
38
豆豆
默认站点
收集整理的這篇文章主要介紹了
Android之AsyncTask两种线程池分析和总结
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Android AsyncTask兩種線程池分析和總結
(一)? ? 前言
在android AsyncTask里面有兩種線程池供我們調用
1.? ? THREAD_POOL_EXECUTOR, 異步線程池
2.? ? SERIAL_EXECUTOR,同步線程池
正如上面名稱描述的那樣,一個是異步線程池,多個任務在線程池中并發執行;還有一個是同步執行的。
默認的話,直接調用execute的話,是使用SERIAL_EXECUTOR
下面的話,會用源代碼的方式來說明這兩種線程池的作用和注意事項。
3.? ? 上面代碼執行的時候會出錯,導致程序異常終止,如下圖
那么,繼續往下面看,看這幾個參數傳進去后是什么意思。
2.? ? 代碼路徑
\libcore\luni\src\main\java\java\util\concurrent\ThreadPoolExecutor.java
代碼: public ThreadPoolExecutor(int corePoolSize, 02int maximumPoolSize, 03long keepAliveTime, 04TimeUnit unit, 05BlockingQueue<Runnable> workQueue, 06ThreadFactory threadFactory, 07RejectedExecutionHandler handler) { 08if (corePoolSize < 0 || 09maximumPoolSize <= 0 || 10maximumPoolSize < corePoolSize || 11keepAliveTime < 0) 12throw new IllegalArgumentException(); 13if (workQueue == null || threadFactory == null || handler == null) 14throw new NullPointerException(); 15this.corePoolSize = corePoolSize; 16this.maximumPoolSize = maximumPoolSize; 17this.workQueue = workQueue; 18this.keepAliveTime = unit.toNanos(keepAliveTime); 19this.threadFactory = threadFactory; 20this.handler = handler; 21} 2223/** 24* The default rejected execution handler 25*/ 26private static final RejectedExecutionHandler defaultHandler = 27new AbortPolicy();
這是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,所以可以直接調用,所以可以有兩種調用方法。
效果是一樣的
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的集合來保證任務的順序執行。
(一)? ? 前言
在android AsyncTask里面有兩種線程池供我們調用
1.? ? THREAD_POOL_EXECUTOR, 異步線程池
2.? ? SERIAL_EXECUTOR,同步線程池
正如上面名稱描述的那樣,一個是異步線程池,多個任務在線程池中并發執行;還有一個是同步執行的。
默認的話,直接調用execute的話,是使用SERIAL_EXECUTOR
下面的話,會用源代碼的方式來說明這兩種線程池的作用和注意事項。
(二)? ???THREAD_POOL_EXECUTOR用法舉例
private static int produceTaskMaxNumber = 500; 02public void dotask(){ 03for (int i = 1; i <= produceTaskMaxNumber; i++){ 04// 產生一個任務,并將其加入到線程池 05String task = "task@ " + i; 06Log.d("Sandy", "put " + task); 07MyAsyncTask asynct = new MyAsyncTask(task); 08asynct.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0); 09} 10} 1112static class MyAsyncTask extends AsyncTask<Integer, Integer, Integer>{ 13private static int consumeTaskSleepTime = 2000; 14// 保存任務所需要的數據 15private Object threadPoolTaskData; 16public MyAsyncTask(String s){ 17threadPoolTaskData = s; 18} 19<a href="http://home.51cto.com/index.php?s=/space/5017954" target="_blank">@Override</a> 20protected Integer doInBackground(Integer... arg0) { 21Log.d("Sandy", "start .." + threadPoolTaskData 22+ " thread id: " + Thread.currentThread().getId() 23+ " thread name: " + Thread.currentThread().getName()); 24try { 25// //便于觀察,等待一段時間 26Thread.sleep(consumeTaskSleepTime); 27} 28catch (Exception e) { 29Log.d("Sandy", "", e); 30} 31threadPoolTaskData = null; 32return 0; 33} 34}2.? ? 使用方法比較簡單,首先創建一個繼承自AsyncTask的MyAsyncTask類,然后調用 MyAsyncTask asynct = new MyAsyncTask(task); 2asynct.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
代碼:
那么,繼續往下面看,看這幾個參數傳進去后是什么意思。
2.? ? 代碼路徑
\libcore\luni\src\main\java\java\util\concurrent\ThreadPoolExecutor.java
代碼: public ThreadPoolExecutor(int corePoolSize, 02int maximumPoolSize, 03long keepAliveTime, 04TimeUnit unit, 05BlockingQueue<Runnable> workQueue, 06ThreadFactory threadFactory, 07RejectedExecutionHandler handler) { 08if (corePoolSize < 0 || 09maximumPoolSize <= 0 || 10maximumPoolSize < corePoolSize || 11keepAliveTime < 0) 12throw new IllegalArgumentException(); 13if (workQueue == null || threadFactory == null || handler == null) 14throw new NullPointerException(); 15this.corePoolSize = corePoolSize; 16this.maximumPoolSize = maximumPoolSize; 17this.workQueue = workQueue; 18this.keepAliveTime = unit.toNanos(keepAliveTime); 19this.threadFactory = threadFactory; 20this.handler = handler; 21} 2223/** 24* The default rejected execution handler 25*/ 26private static final RejectedExecutionHandler defaultHandler = 27new AbortPolicy();
這是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,所以可以直接調用,所以可以有兩種調用方法。
| 1 | a.? ? asynct.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, 0); |
| 2 | b.? ? asynct.execute(0); |
2.執行流程
代碼路徑:
frameworks\base\core\java\android\os\AsyncTask.java
代碼:
| 01 | public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, |
| 02 | ???? ?? ?? ?Params... params) { |
| 03 | ???? ???... |
| 04 | ???? ???exec.execute(mFuture); |
| 05 | ???? ???.... |
| 06 | } |
| 07 | ? |
| 08 | private static class SerialExecutor implements Executor { |
| 09 | ???? ???final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); |
| 10 | ???? ???Runnable mActive; |
| 11 | ???? ???public synchronized void execute(final Runnable r) { |
| 12 | ???? ?? ?? ?mTasks.offer(new Runnable() { |
| 13 | ???? ?? ?? ?? ? public void run() { |
| 14 | ???? ?? ?? ?? ?? ???try { |
| 15 | ???? ?? ?? ?? ?? ?? ?? ?r.run(); |
| 16 | ???? ?? ?? ?? ?? ???} finally { |
| 17 | ???? ?? ?? ?? ?? ?? ?? ?scheduleNext(); |
| 18 | ???? ?? ?? ?? ?? ???} |
| 19 | ???? ?? ?? ?? ? } |
| 20 | ???? ?? ?? ?}); |
| 21 | ???? ?? ?? ?if (mActive == null) { |
| 22 | ???? ?? ?? ?? ? scheduleNext(); |
| 23 | ???? ?? ?? ?} |
| 24 | ???? ???} |
| 25 | ? |
| 26 | ???? ???protected synchronized void scheduleNext() { |
| 27 | ???? ?? ?? ?if ((mActive = mTasks.poll()) != null) { |
| 28 | ???? ?? ?? ?? ? THREAD_POOL_EXECUTOR.execute(mActive); |
| 29 | ???? ?? ?? ?} |
| 30 | ???? ???} |
| 31 | ??? } |
在這個方法里面,它首先把任務放到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的集合來保證任務的順序執行。
總結
以上是默认站点為你收集整理的Android之AsyncTask两种线程池分析和总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android之AIDL使用详解
- 下一篇: 如何将多个Android Wear手表与