在使用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. js清除childNodes中的#text(选项卡中会用到获取第一级子元素)

    我们一般为了代码整洁代码都会换行,如上面所述. 获取div1节点下的childNodes var div = document.getElementById('div1') var child = d ...

  2. Java NIO 系列学习(一)Java NIO概述

    参考资料: http://www.importnew.com/19816.html http://ifeve.com/overview/ NIO 三大核心部分: Channel(通道).Buffer( ...

  3. Kotlin 扩展

    Kotlin 可以对一个类的属性和方法进行扩展,且不需要继承或使用 Decorator 模式. 扩展是一种静态行为,对被扩展的类代码本身不会造成任何影响. 扩展函数 扩展函数可以在已有类中添加新的方法 ...

  4. Oracle创建表、修改字段类型

    1.创建表 1.创建表 create table SCM_PER( --SCM_PER表名 ID ) primary key,--主键ID USERID ),--用户ID --Permission v ...

  5. leecode第二百一十七题(存在重复元素)

    class Solution { public: bool containsDuplicate(vector<int>& nums) { set<int> s; for ...

  6. vlog.hpp

    //vov #ifndef VLOG_HPP #define VLOG_HPP #include <sys/time.h> #include <unistd.h> #ifnde ...

  7. 素数问题(JAVA)

    http://wenda.haosou.com/q/1371348579062596 http://blog.csdn.net/liukehua123/article/details/5482854

  8. HTML中的置换元素和非置换元素

    我们都知道,行内元素不能够定义宽度和高度,但 img,input,button等标签作为行内元素却可以定义宽高,为什么呢?这就牵扯到了置换元素和非置换元素. 置换元素: w3c官方解释:“An ele ...

  9. C++ DWORD 转byte char 数组、指针

    C++ DWORD 转byte char 数组.指针 byte byteData[1024]; // byte数组,示例 DWORD dwData = dwReadBytes; // dword数据, ...

  10. cmd命令往MySQL数据库提交数据

    第一步:MySQL -V检查下载成功否第二步:mysql -u root -p 登陆密码第三步:创建一个数据库 create database if not exists 数据库name: 第四步:展 ...