AsyncTask RejectedExecutionException 小结

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

Java.util.concurrent.RejectedExecutionException: Task [email protected] rejected from [email protected][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 = "[email protected] " + 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 [email protected] rejected from [email protected][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$[email protected] rejected from [email protected][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

同理,如果我将

 private static int TASK_NUMBER = 136;

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

04-25 14:06:21.013 11266-11281/com.ryg.chapter_11 D/jeff: start [email protected] 121 thread id: 277 thread name: AsyncTask #1
04-25 14:06:22.959 11266-11285/com.ryg.chapter_11 D/jeff: start [email protected] 122 thread id: 281 thread name: AsyncTask #5
04-25 14:06:22.960 11266-11289/com.ryg.chapter_11 D/jeff: start [email protected] 124 thread id: 285 thread name: AsyncTask #9
04-25 14:06:22.961 11266-11283/com.ryg.chapter_11 D/jeff: start [email protected] 123 thread id: 279 thread name: AsyncTask #3
04-25 14:06:22.970 11266-11288/com.ryg.chapter_11 D/jeff: start [email protected] 125 thread id: 284 thread name: AsyncTask #8
04-25 14:06:22.970 11266-11286/com.ryg.chapter_11 D/jeff: start [email protected] 126 thread id: 282 thread name: AsyncTask #6
04-25 14:06:22.996 11266-11284/com.ryg.chapter_11 D/jeff: start [email protected] 127 thread id: 280 thread name: AsyncTask #4
04-25 14:06:22.996 11266-11282/com.ryg.chapter_11 D/jeff: start [email protected] 128 thread id: 278 thread name: AsyncTask #2
04-25 14:06:22.996 11266-11287/com.ryg.chapter_11 D/jeff: start [email protected] 129 thread id: 283 thread name: AsyncTask #7
04-25 14:06:23.053 11266-11281/com.ryg.chapter_11 D/jeff: start [email protected] 130 thread id: 277 thread name: AsyncTask #1
04-25 14:06:25.001 11266-11285/com.ryg.chapter_11 D/jeff: start [email protected] 131 thread id: 281 thread name: AsyncTask #5
04-25 14:06:25.001 11266-11283/com.ryg.chapter_11 D/jeff: start [email protected] 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

时间: 2024-10-07 06:38:07

AsyncTask RejectedExecutionException 小结的相关文章

AsyncTask 使用小结

相信各位对 AsyncTask 不会陌生,虽然它有如下弊端: 1. 如果在activiy内部new 一个AsyncTask, 横竖屏切换生成一个新的activity,等结果返回时,处理不好容易出现NPE. 2. 容易出现内存泄漏,如果AsyncTask 进行比较耗时的IO操作(网络操作, 打开一个文件等等),在activity onDestroy的时候没有cancel的话,容易 造成该Activity不能被GC回收(AsyncTask 在Activity内部执行耗时操作). 3. 如果调用 ex

AsyncTask的缺陷以及解决方法

1.AsyncTask常用于进行耗时操作,完成后更新主线程的UI. 2.缺陷:AsyncTask中维护着一个长度为128的线程池,同时可以执行5个工作线程,还有一个缓冲队列,当线程池中已有128个线程,缓冲队列已满时,如果 此时向线程提交任务,将会抛出RejectedExecutionException. 3.解决方法:由一个控制线程来处理AsyncTask的调用判断线程池是否满了,如果满了则线程睡眠否则请求AsyncTask继续处理.

AsyncTask原理

1.对于耗时的操作(如上传下载.读写数据库等),为了不阻塞主线程,我们的一般方法是开启“子线程”.如果需要更新UI,则需要使用handler 2.如果耗时的操作太多,那么我们需要开启太多的子线程,这就会给系统带来巨大的负担,随之也会带来性能方面的问题.在这种情况下我们就可以考虑使用类AsyncTask来异步执行任务,不需要子线程和handler,就可以完成异步操作和刷新UI. 3.AsyncTask的构造方法有三个模板参数 Params(传递给后台任务的参数类型) Progress(后台计算执行

AsyncTask和Handler的优缺点比较(更新线程的问题总结

AsyncTask和Handler的优缺点比较: http://blog.csdn.net/onlyonecoder/article/details/8484200 Handler主要接受子线程发送的数据, 并用此数据配合主线程更新UI. 当应用程序启动时,Android首先会开启一个主线程, 主线程为管理界面中的UI控件,进行事件分发,更新UI只能在主线程中更新,子线程中操作是危险的.这个时候,Handler就需要出来解决这个复杂的问题.由于Handler运行在主线程中(UI线程中),它与子线

Android开发实践:多线程编程小结

我们知道,Android系统为了提高程序的实时响应能力,不允许在UI线程中进行耗时的操作,否则会出现ANR异常,因此必须将耗时的任务放到非UI线程中执行.Android/Java提供了很多类来帮助大家完成异步操作,比如:Thread类,Timer类,AsyncTask类,HandlerThread类,以及Executor接口.这些类都分别在什么场合下使用呢? 本文简单地总结一下Android开发中常见的多线程类型和解决方案,并比较和分析了各个方案的区别,以便更好地理解和应用这些API接口. 1.

AsyncTask - 基本原理 图文剖析

最近用到了AsyncTask,这玩意每个写android程序的都会用,可是不见得每个人都能用的好.如果想要用好,那么首先势必对基本原理有个大概了解.其实网上对这类问题的说明已经很多很多了,这里我就用自己的思维整理一下. AsyncTask概述 AsyncTask是google公司封装的一个轻量级的异步任务类.实际上它内部也是通过Thread + handler实现的.如果没有AsyncTask类,我们完全可以用thread+handler来处理.这个时候就很可能自己回去封装一下thread+ha

Android笔记(三十六) AsyncTask是如何执行的?

在上一个例子中,我们是在LoadImage的onPostExecute中修改的UI,不是说只允许在主线程中修改UI吗?我们看一下源代码是如何操作的. MainActicity.java package cn.lixyz.asynctest; import android.app.Activity; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask;

Android 结合实例学会AsyncTask的用法

AsyncTask执行时经过四个步骤,执行四个方法: 1.onPreExecute(),运行在UI线程,可以设置或修改UI控件,如显示一个进度条 2.doInBackground,运行在后台线程,不可以设置或修改UI控件,该方法的执行时机是:onPreExecute()执行完毕立即调用该方法,在方法中进行耗时操作,可以在该方法中调用publishProgress方法来发布当执行的进度,调用publishProgress方法后就会立即触发onProgressUpdate方法 3.onProgres

Android AsyncTask 封装初步一

AsyncTask基本使用 先从最熟悉的Task使用开始说起,给出LastDaysTask作为实例如下 @SuppressLint("NewApi") private class LastDaysTask extends AsyncTask<Void, Integer, Boolean>{ @Override protected void onPreExecute() { //TODO UI线程或者主线程 } @Override protected Boolean doIn