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: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加载图片的时候特别慢,具体细节,请参考如下 博客:
AsyncTask RejectedExecutionException 小结的更多相关文章
- AsyncTask 进行耗时操作和UI 更新
相信各位对 AsyncTask 不会陌生,虽然它有如下弊端: 1. 如果在activiy内部new 一个AsyncTask, 横竖屏切换生成一个新的activity,等结果返回时,处理不好容易出现NP ...
- AsyncTask的缺陷以及解决方法
1.AsyncTask常用于进行耗时操作,完成后更新主线程的UI. 2.缺陷:AsyncTask中维护着一个长度为128的线程池,同时可以执行5个工作线程,还有一个缓冲队列,当线程池中已有128个线程 ...
- Android 结合实例学会AsyncTask的用法
AsyncTask执行时经过四个步骤,执行四个方法: 1.onPreExecute(),运行在UI线程,可以设置或修改UI控件,如显示一个进度条 2.doInBackground,运行在后台线程,不可 ...
- AsyncTask 解析
[转载自 http://blog.csdn.net/yanbober ] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handl ...
- AsyncTask两种线程池
AsyncTask两种线程池 http://bbs.51cto.com/thread-1114378-1.html (API 3.0以后): 1.THREAD_POOL_EXECUTOR, ...
- android 的线程模型和AsyncTask
android 的线程模型:当一个 android 的应用运行后,就会有一个 UI 的 main 线程启动 , 这是一个非常重要的线程,它负责把事件分派到相应的控件,其中就包括屏幕绘图 ...
- Android多线程任务优化1:探讨AsyncTask的缺陷
AsyncTask还有别的缺陷,在生成listview的时候,如果adapter里面的count动态改变的话,不能使用AsyncTask,只能使用Thread+Handler,否则会出现如下错误 j ...
- Android 结合实例学会AsyncTask的使用方法
AsyncTask运行时经过四个步骤,运行四个方法: 1.onPreExecute(),执行在UI线程,能够设置或改动UI控件,如显示一个进度条 2.doInB ...
- AsyncTask兼容性
简介 AsyncTask是Android系统提供的异步方式,其优点在于在子线程执行任务,并将结果传递给主线程. 实现方式 AsyncTask封装了Executor和Handler. 基本使用 通过As ...
随机推荐
- 在微信浏览器中 location.reload() 不刷新解决方案(直接调用方法)
1.问题 在微信浏览器中,需要时刷新当前页面. 正常情况下我们直接使用 location.reload 方法来刷新. 2.解决方法 function realod(){ var {search,hre ...
- Xilinx Vivado的使用详细介绍(5):调用用户自定义封装的IP核
Zedboard OLED Display Controller IP v1 介绍 Author:zhangxianhe 本文档提供了快速添加,连接和使用ZedboardOLED v1.0 IP内核的 ...
- power shell 脚本了解
1. https://www.cnblogs.com/xianglongsdu/p/5832984.html 2.https://www.cnblogs.com/lsdb/p/9531338.html ...
- 在Mac os 10.11 下编译Berkeley caffe
安装各种补丁和组件,缺啥装啥. python 采用 2.7.13 最新版. 安装工具 homebrew , pip 很繁琐,但是没难度. 由于本人macbook pro不支持CUDA,所以不用安装. ...
- layui 根据根据后台数据动态创建下拉框并同时默认选中
第一步 form表单里写好一个下拉框 <div class="layui-form-item"> <label class="layui-for ...
- ASP.NET MVC 目录介绍
- 利用Python进行数据分析(第二版)电子版书籍分享
资料下载地址: 链接:https://pan.baidu.com/s/1y1C0bJPkSn7Sv6Eq9G5_Ug 提取码:vscu <利用Python进行数据分析(第二版)>高清中文版 ...
- EXCE 表格导入导出遇到问题(easypoi)
使用Easypoi进行excel表格的导入导出遇到的问题: 1.导出时候打开文件一直遇乱码,主要的原因就是我在实体类没有进行给每个字段进行注解,就会导致每个字段找不到对应的汉字表头,所以一定不要忘了导 ...
- Python3 NameError: name 'open' is not defined处理办法
一.说明 之前默认以为python的open方法用存哪里都没什么区别的,然后昨天直接在"__del__()"中使用今天同事跑程序时反馈程序报错“Python3 NameError: ...
- SQLite数据库 简介、特点、优势、局限性及使用
SQLite简介 SQLite是一个进程内的轻量级嵌入式数据库,它的数据库就是一个文件,实现了自给自足.无服务器.零配置的.事务性的SQL数据库引擎.它是一个零配置的数据库,这就体现出来SQLite与 ...