在使用Asynctask時,相信有些朋友會遇到如下RejectedExecutionException:java
Java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@e3a9753 rejected from java.util.concurrent.ThreadPoolExecutor@63fe890[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0] at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) at android.app.ActivityThread.-wrap11(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
究竟是什麼原理致使以上RuntimeException呢? 讓咱們一塊兒研讀源碼來一探究竟。android
首先,能夠使用如下代碼來製造上面的Exception:app
package com.breakmedia.interview.asyncTask; import android.os.AsyncTask; import android.util.Log; public class AsyncPool { private static int TASK_NUMBER = 138; private static final String TAG = "jeff"; public void doTask() { for (int i = 0; i <= TASK_NUMBER; i++) { String task = "task@ " + i; Log.d(TAG, "put " + task); MyAsyncTask myAsyncTask = new MyAsyncTask(task); myAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0); } } static class MyAsyncTask extends AsyncTask<Integer, Integer, Integer> { private static int SLEEP_TIME = 2000; private String name; public MyAsyncTask(String name) { this.name = name; } protected Integer doInBackground(Integer... arg0) { Log.d(TAG, "start .." + name + " thread id: " + Thread.currentThread().getId() + " thread name: " + Thread.currentThread().getName()); try { Thread.sleep(SLEEP_TIME); } catch (Exception e) { Log.d(TAG, "", e); } return 0; } } }
須要解釋的是,我用的CPU 是四核,因此對應的 MAXIMUM_POOL_SIZE 爲9, async
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
而在AsyncTask 內部有一個static 的變量 ThreadPoolExecutor, 其 workQuene 爲容量爲128的 LinkedBlockingQueue:函數
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128); /** * An {@link Executor} that can be used to execute tasks in parallel. */ public static final Executor THREAD_POOL_EXECUTOR; static { ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); threadPoolExecutor.allowCoreThreadTimeOut(true); THREAD_POOL_EXECUTOR = threadPoolExecutor; }
綜上所述,若是按照 myAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0);oop
調用,若是Task_NUMBER 設置爲138,應該會出現RejectException, 結果是否是和預想同樣的呢? 運行程序,若是獲得同樣的RuntimeException測試
java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@63fe890 rejected from java.util.concurrent.ThreadPoolExecutor@1721589[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0] at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) at android.app.ActivityThread.-wrap11(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) Caused by: java.util.concurrent.RejectedExecutionException: Task android.os.AsyncTask$3@63fe890 rejected from java.util.concurrent.ThreadPoolExecutor@1721589[Running, pool size = 9, active threads = 9, queued tasks = 128, completed tasks = 0] at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2014) at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:794) at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1340) at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:607) at com.breakmedia.interview.asyncTask.AsyncPool.doTask(AsyncPool.java:16) at com.ryg.chapter_11.MainActivity.onCreate(MainActivity.java:91) at android.app.Activity.performCreate(Activity.java:6237) at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107) at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) at android.app.ActivityThread.-wrap11(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 04-25 13:55:12.788 763-833/system_process E/Surface: getSlotFromBufferLocked: unknown buffer: 0xde3024d0
同理,若是我將this
private static int TASK_NUMBER = 136;
是否是程序正常運行呢?我的測試下來,沒有碰到crash。google
04-25 14:06:21.013 11266-11281/com.ryg.chapter_11 D/jeff: start ..task@ 121 thread id: 277 thread name: AsyncTask #1 04-25 14:06:22.959 11266-11285/com.ryg.chapter_11 D/jeff: start ..task@ 122 thread id: 281 thread name: AsyncTask #5 04-25 14:06:22.960 11266-11289/com.ryg.chapter_11 D/jeff: start ..task@ 124 thread id: 285 thread name: AsyncTask #9 04-25 14:06:22.961 11266-11283/com.ryg.chapter_11 D/jeff: start ..task@ 123 thread id: 279 thread name: AsyncTask #3 04-25 14:06:22.970 11266-11288/com.ryg.chapter_11 D/jeff: start ..task@ 125 thread id: 284 thread name: AsyncTask #8 04-25 14:06:22.970 11266-11286/com.ryg.chapter_11 D/jeff: start ..task@ 126 thread id: 282 thread name: AsyncTask #6 04-25 14:06:22.996 11266-11284/com.ryg.chapter_11 D/jeff: start ..task@ 127 thread id: 280 thread name: AsyncTask #4 04-25 14:06:22.996 11266-11282/com.ryg.chapter_11 D/jeff: start ..task@ 128 thread id: 278 thread name: AsyncTask #2 04-25 14:06:22.996 11266-11287/com.ryg.chapter_11 D/jeff: start ..task@ 129 thread id: 283 thread name: AsyncTask #7 04-25 14:06:23.053 11266-11281/com.ryg.chapter_11 D/jeff: start ..task@ 130 thread id: 277 thread name: AsyncTask #1 04-25 14:06:25.001 11266-11285/com.ryg.chapter_11 D/jeff: start ..task@ 131 thread id: 281 thread name: AsyncTask #5 04-25 14:06:25.001 11266-11283/com.ryg.chapter_11 D/jeff: start ..task@ 132 thread id: 279 thread name: AsyncTask #3
讓咱們看看AsyncTask爲何到達閾值時,這個RuntimeException的調用過程:spa
ThreadPoolExecutor 的構造函數以下:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler); }
其中defaultHandler的實現以下:
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
其中AbortPolicy的源碼以下:
/** * A handler for rejected tasks that throws a * {@code RejectedExecutionException}. */ public static class AbortPolicy implements RejectedExecutionHandler { /** * Creates an {@code AbortPolicy}. */ public AbortPolicy() { } /** * Always throws RejectedExecutionException. * * @param r the runnable task requested to be executed * @param e the executor attempting to execute this task * @throws RejectedExecutionException always */ public void rejectedExecution(Runnable r, ThreadPoolExecutor e) { throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString()); } }
因此你們如今應該知道 RejectedExecutionException 是如何產生的吧。
最後須要強調一下,其實google很早就意識到這個問題,因此默認的方式是
myAsyncTask.execute(0);
而不是
myAsyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0);
使用的默認方式,不會出現RejectedExecutionException,即便 TASK_NUMBER = 500, 不信的同志能夠本身試驗一下。究其緣由,能夠看如下代碼:
@MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor { final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); Runnable mActive; public synchronized void execute(final Runnable r) { mTasks.offer(new Runnable() { public void run() { try { r.run(); } finally { scheduleNext(); } } }); if (mActive == null) { scheduleNext(); } } protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } }
從以上代碼能夠看出,由於 execute 和 scheduleNext 都加了鎖,線程應該是串行執行,而不是並行執行,THREAD_POOL_EXECUTOR.execute(mActive) 每次執行一個,固然不會到達峯值(137)。可是因爲是串行執行,又會帶來一個新的問題,有人用AsyncTask加載圖片的時候特別慢,具體細節,請參考以下 博客: