Android AsyncTask两种线程池分析和总结
转自:http://bbs.51cto.com/thread-1114378-1-1.html
Android AsyncTask两种线程池分析和总结
(一) 前言
在android AsyncTask里面有两种线程池供我们调用
1. THREAD_POOL_EXECUTOR, 异步线程池
2. SERIAL_EXECUTOR,同步线程池
正如上面名称描述的那样,一个是异步线程池,多个任务在线程池中并发执行;还有一个是同步执行的。
默认的话,直接调用execute的话,是使用SERIAL_EXECUTOR
下面的话,会用源代码的方式来说明这两种线程池的作用和注意事项。
(二) THREAD_POOL_EXECUTOR用法举例
1. 代码
01
|
private static int produceTaskMaxNumber = 500 ;
|
02
|
public void dotask(){
|
03
|
for ( int i = 1 ; i <= produceTaskMaxNumber; i++){
|
04
|
// 产生一个任务,并将其加入到线程池
|
05
|
String task = "task@ " + i;
|
06
|
Log.d( "Sandy" , "put " + task);
|
07
|
MyAsyncTask asynct = new MyAsyncTask(task);
|
08
|
asynct.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0 );
|
09
|
}
|
10
|
}
|
11
|
12
|
static class MyAsyncTask extends AsyncTask<Integer, Integer, Integer>{
|
13
|
private static int consumeTaskSleepTime = 2000 ;
|
14
|
// 保存任务所需要的数据
|
15
|
private Object threadPoolTaskData;
|
16
|
public MyAsyncTask(String s){
|
17
|
threadPoolTaskData = s;
|
18
|
}
|
19
|
<a href= "http://home.51cto.com/index.php?s=/space/5017954" target= "_blank" > @Override </a>
|
20
|
protected Integer doInBackground(Integer... arg0) {
|
21
|
Log.d( "Sandy" , "start .." + threadPoolTaskData
|
22
|
+ " thread id: " + Thread.currentThread().getId()
|
23
|
+ " thread name: " + Thread.currentThread().getName());
|
24
|
try {
|
25
|
// //便于观察,等待一段时间
|
26
|
Thread.sleep(consumeTaskSleepTime);
|
27
|
}
|
28
|
catch (Exception e) {
|
29
|
Log.d( "Sandy" , "" , e);
|
30
|
}
|
31
|
threadPoolTaskData = null ;
|
32
|
return 0 ;
|
33
|
}
|
34
|
}
|
2. 使用方法比较简单,首先创建一个继承自AsyncTask的MyAsyncTask类,然后调用
1
|
MyAsyncTask asynct = new MyAsyncTask(task);
|
2
|
asynct.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0 );
|
就可以了。
3. 上面代码执行的时候会出错,导致程序异常终止,如下图
原因是:
就是因为我们尝试添加500个task到AsyncTask.THREAD_POOL_EXECUTOR线程池中,但是它的核心线程是5,队列容量是128,最大线程数是9。
所以,抛出了这个异常。
那么,接下来的话,我们会去分析这个异常怎么出来的。
(三) THREAD_POOL_EXECUTOR代码分析
从AsyncTask.THREAD_POOL_EXECUTOR的定义开始分析
1. 代码路径
frameworks\base\core\java\android\os\AsyncTask.java
代码:
01
|
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
|
02
|
private static final int CORE_POOL_SIZE = CPU_COUNT + 1 ;
|
03
|
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1 ;
|
04
|
private static final int KEEP_ALIVE = 1 ;
|
05
|
06
|
....
|
07
|
/**
|
08
|
* An {@link Executor} that can be used to execute tasks in parallel.
|
09
|
*/
|
10
|
public static final Executor THREAD_POOL_EXECUTOR
|
11
|
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
|
12
|
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
|
它的几个参数CORE_POOL_SIZE, MAXIMUN_POOL_SIZE, 都是根据当前手机的处理器数量进行动态定义的。
那么,继续往下面看,看这几个参数传进去后是什么意思。
2. 代码路径
\libcore\luni\src\main\java\java\util\concurrent\ThreadPoolExecutor.java
代码:
01
|
public ThreadPoolExecutor( int corePoolSize,
|
02
|
int maximumPoolSize,
|
03
|
long keepAliveTime,
|
04
|
TimeUnit unit,
|
05
|
BlockingQueue<Runnable> workQueue,
|
06
|
ThreadFactory threadFactory,
|
07
|
RejectedExecutionHandler handler) {
|
08
|
if (corePoolSize < 0 ||
|
09
|
maximumPoolSize <= 0 ||
|
10
|
maximumPoolSize < corePoolSize ||
|
11
|
keepAliveTime < 0 )
|
12
|
throw new IllegalArgumentException();
|
13
|
if (workQueue == null || threadFactory == null || handler == null )
|
14
|
throw new NullPointerException();
|
15
|
this .corePoolSize = corePoolSize;
|
16
|
this .maximumPoolSize = maximumPoolSize;
|
17
|
this .workQueue = workQueue;
|
18
|
this .keepAliveTime = unit.toNanos(keepAliveTime);
|
19
|
this .threadFactory = threadFactory;
|
20
|
this .handler = handler;
|
21
|
}
|
22
|
23
|
/**
|
24
|
* The default rejected execution handler
|
25
|
*/
|
26
|
private static final RejectedExecutionHandler defaultHandler =
|
27
|
new AbortPolicy();
|
这是ThreadPoolExecutor的构造函数,首先需要明白的是这几个参数的含义
A. corePoolSize: 线程池维护线程的最少数量
B. maximumPoolSize:线程池维护线程的最大数量
C. keepAliveTime: 线程池维护线程所允许的空闲时间
D. unit: 线程池维护线程所允许的空闲时间的单位
E. workQueue: 线程池所使用的缓冲队列
F. handler: 线程池对拒绝任务的处理策略
当一个任务通过asynct.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
也就是:处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:
NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue是BlockQueue的子类,ArrayBlockingQueue,DelayQueue
handler有四个选择(这不是android的Handler):
ThreadPoolExecutor.AbortPolicy() – 这个也是AsyncTask.THREAD_POOL_EXECUTOR使用的
抛出java.util.concurrent.RejectedExecutionException异常
ThreadPoolExecutor.CallerRunsPolicy()
重试添加当前的任务,他会自动重复调用execute()方法
ThreadPoolExecutor.DiscardOldestPolicy()
抛弃旧的任务
ThreadPoolExecutor.DiscardPolicy()
抛弃当前的任务
所以,正是我们的AsyncTask.THREAD_POOL_EXECUTOR使用了AbortPolicy()类型的handler,所以才会抛出异常..
那么,在把任务添加到AsyncTask.THREAD_POOL_EXECUTOR之后,下面的工作就是由这个线程池来调度线程执行任务了。
(四) AsyncTask. SERIAL_EXECUTOR
1. 使用方法
AsyncTask. SERIAL_EXECUTOR的使用方法和Async.THREAD_POOL_EXECUTOR差不多。不过正如前面所说,它是默认的Executor,所以可以直接调用,所以可以有两种调用方法。
1
|
a. asynct.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, 0 );
|
2
|
b. asynct.execute( 0 );
|
效果是一样的
2.执行流程
代码路径:
frameworks\base\core\java\android\os\AsyncTask.java
代码:
01
|
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
|
02
|
Params... params) {
|
03
|
...
|
04
|
exec.execute(mFuture);
|
05
|
....
|
06
|
}
|
07
|
08
|
private static class SerialExecutor implements Executor {
|
09
|
final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
|
10
|
Runnable mActive;
|
11
|
public synchronized void execute( final Runnable r) {
|
12
|
mTasks.offer( new Runnable() {
|
13
|
public void run() {
|
14
|
try {
|
15
|
r.run();
|
16
|
} finally {
|
17
|
scheduleNext();
|
18
|
}
|
19
|
}
|
20
|
});
|
21
|
if (mActive == null ) {
|
22
|
scheduleNext();
|
23
|
}
|
24
|
}
|
25
|
26
|
protected synchronized void scheduleNext() {
|
27
|
if ((mActive = mTasks.poll()) != null ) {
|
28
|
THREAD_POOL_EXECUTOR.execute(mActive);
|
29
|
}
|
30
|
}
|
31
|
}
|
嗯,它会调用到SerialExecutor.execute(Runnable r)方法
在这个方法里面,它首先把任务放到mTasks这个集合里面;然后判断mActivie是否为空,再调用scheduleNext ()方法。
mActivie为null的意思是当前没有任务在执行,如果mActivie!=null,那么说明当前有任务正在执行,那么只要把任务添加到mTasks里面即可。
因为任务执行完毕后,会再次调用scheduleNext()方法的,就是
finally {
scheduleNext();
}
这样就形成了一种链状调用结构,只要mTasks里面还有任务,就会不断逐一调用,如果后面有任务进来,就只要添加到mTasks里面即可。
同时,不知道大家注意到没有,这两个方法都是synchronized的,这样,就保证了多线程之间调度问题。
否则肯定会出现问题的,至于什么问题,大家想想就能明白。
4. 继续分析scheduleNext()方法
这个方法首先把mTasks里面的数据取一个出来,然后调用
THREAD_POOL_EXECUTOR.execute(mActive);
我晕,这不就是上面一直在分析的AsyncTask.THREAD_POOL_EXECUTOR么?
好吧,原来AsyncTask.THREAD_POOL_EXECUTOR和AsyncTask.SERIAL_EXECUTOR的区别就是SERIAL_EXECUTOR在THREAD_POOL_EXECUTOR的基础上添加了一个mTasks的集合来保证任务顺序执行而已...
(五) 总结
说了这么多,总结下
1. AsyncTask里面有THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR两种方式来异步执行任务;THREAD_POOL_EXECUTOR是异步的,而SERIAL_EXECUTOR任务是顺序执行的。
2. THREAD_POOL_EXECUTOR如果添加的任务过多,没有及时处理的话,会导致程序崩溃,它的队列size是128;它的调度规则是核心池大小,队列大小,以及最大线程数和异常处理Handler来决定的。
3. SERIAL_EXECUTOR本质是在THREAD_POOL_EXECUTOR的基础上添加一个mTasks的集合来保证任务的顺序执行。
Android AsyncTask两种线程池分析和总结相关推荐
- Android之AsyncTask两种线程池分析和总结
Android AsyncTask两种线程池分析和总结 (一) 前言 在android AsyncTask里面有两种线程池供我们调用 1. THREAD_POOL_EXECUTOR, 异步 ...
- android AsyncTask 只能在线程池里单个运行的问题
android 的AysncTask直接调用Execute会在在一个线程池里按调用的先后顺序依次执行. 如果应用的所有网络获取都依赖这个来做,当有一个网络请求柱塞,就导致其它请求也柱塞了. 在3.0 ...
- android asynctask源码分析,Android通过Handler与AsyncTask两种方式动态更新ListView(附源码)...
本文实例讲述了Android通过Handler与AsyncTask两种方式动态更新ListView的方法.分享给大家供大家参考,具体如下: 有时候我们需要修改已经生成的列表,添加或者修改数据,noti ...
- Android中Callable、Future、FutureTask的概念以及几种线程池的使用
学习线程池必备知识: 在开始介绍线程池之前,先来介绍下Callable和Future的概念,众所周知,Android中实现多线程的方式有两种,实现Runnable接口或者继承一个Thread,但是这两 ...
- Java 四种线程池的用法分析
1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() {@Overridepublic void run() {// ...
- java线程池饱和策略_干货:Java几种线程池的分析和使用。
原标题:干货:Java几种线程池的分析和使用. 利用线程池的优势: 1.降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 2.提高响应速度.当任务到达时,任务可以不需要等到线程创建 ...
- [转]new Thread的弊端及Java四种线程池的使用
介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端 执行一个异步任务你还只是如下new ...
- java线程池详解及五种线程池方法详解
基础知识 Executors创建线程池 Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThrea ...
- Java ExecutorService四种线程池的例子与说明
1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() {@Overridepublic void run() {// ...
最新文章
- 在VS2010中创建自定义的代码段
- 初中人教版电子课本app_电子课本|2020秋 部编人教版初中历史七年级上册教材电子课本(高清更新可打印)...
- mysql外键约束分数_MySQL提高(外键约束)
- 基于机器视觉的马达孔直径中心距、齿数线序测量
- robotlegs框架吴秦大师续讲【转载】
- Pytorch中Tensor和numpy数组的互相转化
- apache 支持.htaccess 解决Internal Server Error The server 错误
- java毕业设计题目大全
- libtorrent源码分析(三)BT下载相关知识总结
- 排查 java 程序CPU飙升问题
- (十八)视频换-脸、无训练高速换-脸、一张图片即可完成、批量处理
- Monkey King - 左偏树
- 01.Windows系统安装
- fixture 'xxx' not found
- windows中cmd和powershell设置代理
- 格式化字符串漏洞及利用_萌新食用
- 最短路径的模板-摘自宫水三叶
- 已解决Could not install packages due to an EnvirommentError:[WinError 5]拒绝访问。:‘c: [programdata | lanaco
- HTML5七夕情人节表白网页(幻化3D相册) HTML+CSS+JavaScript 求婚示爱代码 520情人节告白代码 程序员表白源码 3D旋转相册 js烟花代码 css爱心表白
- CSS系列之CSS选择器
热门文章
- docker harbor 域名_超详细的搭建docker私服Harbor教程
- 新疆大学(新大)OJ xju 1006: 比赛排名 第二类斯特林数+阶乘
- SVN下载谷歌上的代码
- struts2关键配置及函数总结,
- C 程序更有效率的 10 种方法
- hihoCoder #1467 : 2-SAT·hihoCoder音乐节
- 坑 之 tensorflow使用sess.run处理图片时越来越慢,占用内存越来越大的问题
- c++ 模板教程(c语言中文网) 自己运行实例
- 锐捷服务器虚拟化技术_数据中心边缘虚拟交换(IEEE 802.1Qbg)技术白皮书
- 机器学习-单个感知器实现逻辑或(matlab实现)