• AsyncTask RejectedExecutionException 小结


    在使用Asynctask时,相信有些朋友会遇到以下RejectedExecutionException:

    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呢? 让我们一起研读源码来一探究竟。

    首先,可以使用以下代码来制造上面的Exception:

    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, 

    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);

    调用,如果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:61604-25 13:55:12.788 763-833/system_process E/Surface: getSlotFromBufferLocked: unknown buffer: 0xde3024d0

    同理,如果我将

     private static int TASK_NUMBER = 136;

    是不是程序正常运行呢?个人测试下来,没有碰到crash。

    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的调用过程:

    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加载图片的时候特别慢,具体细节,请参考如下 博客:

    Android实战技巧:深入解析AsyncTask

  • 相关阅读:
    233. Number of Digit One
    232. Implement Queue using Stacks
    231. Power of Two
    230. Kth Smallest Element in a BST
    229. Majority Element II
    228. Summary Ranges
    227. Basic Calculator II
    ArcGIS 网络分析[3] 发布NAServer到ArcGIS for Server(以Server 10.4为例)
    iView的使用【小白向】
    Vuejs环境安装与工程建立【小白Windows向】
  • 原文地址:https://www.cnblogs.com/budoudou/p/6762361.html
Copyright © 2020-2023  润新知