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 ...
随机推荐
- Kafka作为大数据的核心技术,你了解多少?
Kafka作为大数据最核心的技术,作为一名技术开发人员,如果你不懂,那么就真的“out”了.DT时代的快速发展离不开kafka,所以了解kafka,应用kafka就成为一种必须. 什么是kafka?K ...
- cookie 和session 详解
cookie机制采用的是在客户端保持状态的方案,而session机制采用的是在服务器端保持状态的方案. 由于采用服务器端保持状态的方案在客户端也需要保存一个标识,所以session机制可能需要借助于c ...
- 2018-2019-2 网络对抗技术 20165303 Exp4 恶意代码分析
实践目标 1.1是监控你自己系统的运行状态,看有没有可疑的程序在运行. 1.2是分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具尽量使用原生指令或sysinternals,systr ...
- .NetCore WebApi 添加 Log4Net
一 .配置 1.vs2019 创建一个.net core web程序,选择webapi 2.项目中添加一个配置文件:添加--新建项--XML文件,命名为log4net.config 我使用的是log4 ...
- BootStrap 学习笔记一
1.bootstrap列偏移 列偏移是指在网格系统中,可以使用偏移列来达到让某列右移的效果,只需要在class的“col-*-*”后追加“col-*-offset-*” <div class=& ...
- docker 容器的mysql主从复制
一. 1.首先拉取docker镜像,我们这里使用5.7版本的mysql: docker pull mysql:5.7 2.分别启动主从两个容器: docker run -p 3339:3306 - ...
- Oracle单机Rman笔记[5]---脱机异地还原
脱机异地还原(安装一个原环境相同的linux,并安装数据库,注意不要配置安装实例) .检查/home/oracle下的.bashrc .bash_profile内容是否与原环境一致(具体看情况而定), ...
- .NET Core和Swagger 生成 Api 文档转
阅读目录 1.引用 2.打开startup.cs文件 3.设置XML注释 4.运行结果 5.主要问题的解决办法 6.可以自定义UI 前言 最近写了好多Web api, 老大说太乱了,要整理一下,使用S ...
- node.js安装express模块应用服务框架
1.创建工程文件夹case-04 2.在终端窗口进入文件夹目录,并输入:npm init,并一路回车,最后看到在case-04文件夹里自动生成了package.json 文件 3.打开vscode,进 ...
- css的em是根据什么来写的
em是要依靠父元素的值来计算的 1.字体计算公式依旧 1 ÷ 父元素的font-size × 需要转换的像素值 = em值 转自:https://zhidao.baidu.com/question/7 ...