在使用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: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 ..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

AsyncTask RejectedExecutionException 小结的更多相关文章

  1. AsyncTask 进行耗时操作和UI 更新

    相信各位对 AsyncTask 不会陌生,虽然它有如下弊端: 1. 如果在activiy内部new 一个AsyncTask, 横竖屏切换生成一个新的activity,等结果返回时,处理不好容易出现NP ...

  2. AsyncTask的缺陷以及解决方法

    1.AsyncTask常用于进行耗时操作,完成后更新主线程的UI. 2.缺陷:AsyncTask中维护着一个长度为128的线程池,同时可以执行5个工作线程,还有一个缓冲队列,当线程池中已有128个线程 ...

  3. Android 结合实例学会AsyncTask的用法

    AsyncTask执行时经过四个步骤,执行四个方法: 1.onPreExecute(),运行在UI线程,可以设置或修改UI控件,如显示一个进度条 2.doInBackground,运行在后台线程,不可 ...

  4. AsyncTask 解析

    [转载自 http://blog.csdn.net/yanbober ] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handl ...

  5. AsyncTask两种线程池

        AsyncTask两种线程池  http://bbs.51cto.com/thread-1114378-1.html (API 3.0以后): 1.THREAD_POOL_EXECUTOR, ...

  6. android 的线程模型和AsyncTask

            android 的线程模型:当一个 android 的应用运行后,就会有一个 UI 的 main 线程启动 , 这是一个非常重要的线程,它负责把事件分派到相应的控件,其中就包括屏幕绘图 ...

  7. Android多线程任务优化1:探讨AsyncTask的缺陷

     AsyncTask还有别的缺陷,在生成listview的时候,如果adapter里面的count动态改变的话,不能使用AsyncTask,只能使用Thread+Handler,否则会出现如下错误 j ...

  8. Android 结合实例学会AsyncTask的使用方法

    AsyncTask运行时经过四个步骤,运行四个方法:           1.onPreExecute(),执行在UI线程,能够设置或改动UI控件,如显示一个进度条           2.doInB ...

  9. AsyncTask兼容性

    简介 AsyncTask是Android系统提供的异步方式,其优点在于在子线程执行任务,并将结果传递给主线程. 实现方式 AsyncTask封装了Executor和Handler. 基本使用 通过As ...

随机推荐

  1. C语言--第3次作业

    1.本章学习总结 1.1思维导图 1.2 本章学习体会及代码量学习体会 1.2.1学习体会 我眼中的函数 这一章接触到了新的知识--函数,不同于之前所学的编程,函数具有很清楚的分工性,且可以用来多次调 ...

  2. libc++abi.dylib: terminating with uncaught exception of type NSException (lldb)

    In Xcode 9 and Swift 4: Print exception stack to know the reason of the exception: Go to show break ...

  3. 搜索引擎选择: Elasticsearch与Solr(转)

    搜索引擎选型调研文档 Elasticsearch简介* Elasticsearch是一个实时的分布式搜索和分析引擎.它可以帮助你用前所未有的速度去处理大规模数据. 它可以用于全文搜索,结构化搜索以及分 ...

  4. ubuntu使用抓包工具,charles

    参考官网:https://www.charlesproxy.com/documentation/installation/apt-repository/ wget -q -O - https://ww ...

  5. sqlite3如何判断一个表是否已经存在于数据库中 C++

    SELECT count(*) AS cnt FROM sqlite_master WHERE type='table' AND name='table_name';cnt will return 0 ...

  6. APP多开教程

    https://bbs.feng.com/read-htm-tid-11793134.html 本教程原创,送给有需要的人.本人就为了多开百度云盘传数据,故多开两个百度云盘,亲测一周多没毛病.微信多开 ...

  7. windows下载mysql5.7压缩包配置安装mysql

    windows安装mysql5.7有两种方式 1.下载.msi安装文件直接根据界面提示进行安装 2.下载.biz或.zip压缩包 注:本次在win10系统下使用压缩包进行安装mysql5.7 mysq ...

  8. angular 引入编辑器以及控制器的学习和理解。。。

    在angular中引入编辑器的时候花了很长时间,然后发现自己以前根本就没好好用过angular,因为项目是接手的学姐的,学姐又是接手的学姐的,到我这里就只是写写页面的事了. 引入编辑器差了好多好多资料 ...

  9. GET 和 POST 的区别 以及为什么 GET请求 比 POST请求 更快

    引子: 和朋友的聊天中得知他公司后台接口全部都是 POST 请求, 我表示很纳闷为什么全是 POST 请求呢? GET 比 POST 安全,或者说 便于后台方便,后台不用区分包装类  (所以全部用 P ...

  10. LaLeX数学公式

    启用数学公式: 需要插入公式时,用 $ 将公式包围.若需要输入多行,则用一对 $$ 包围. 例如: $$ \rho = \sqrt{(\Delta x)^{2}+(\Delta y)^{2}} \\ ...