Android AsyncTask两种线程池分析和总结
(一)    前言
在android AsyncTask里面有两种线程池供我们调用
1.    THREAD_POOL_EXECUTOR, 异步线程池
2.    SERIAL_EXECUTOR,同步线程池
正如上面名称描述的那样,一个是异步线程池,多个任务在线程池中并发执行;还有一个是同步执行的。
默认的话,直接调用execute的话,是使用SERIAL_EXECUTOR
下面的话,会用源代码的方式来说明这两种线程池的作用和注意事项。

(二)     THREAD_POOL_EXECUTOR用法举例

private static int produceTaskMaxNumber = 500;
02public void dotask(){
03for (int i = 1; i <= produceTaskMaxNumber; i++){
04// 产生一个任务,并将其加入到线程池
05String task = "task@ " + i;
06Log.d("Sandy", "put " + task);
07MyAsyncTask asynct = new MyAsyncTask(task);
08asynct.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, 0);
09}
10}
1112static class MyAsyncTask extends AsyncTask<Integer, Integer, Integer>{
13private static int consumeTaskSleepTime = 2000;
14// 保存任务所需要的数据
15private Object threadPoolTaskData;
16public MyAsyncTask(String s){
17threadPoolTaskData = s;
18}
19<a href="http://home.51cto.com/index.php?s=/space/5017954" target="_blank">@Override</a>
20protected Integer doInBackground(Integer... arg0) {
21Log.d("Sandy", "start .." + threadPoolTaskData
22+ " thread id: " + Thread.currentThread().getId()
23+ " thread name: " + Thread.currentThread().getName());
24try {
25// //便于观察,等待一段时间
26Thread.sleep(consumeTaskSleepTime);
27}
28catch (Exception e) {
29Log.d("Sandy", "", e);
30}
31threadPoolTaskData = null;
32return 0;
33}
34}

2.    使用方法比较简单,首先创建一个继承自AsyncTask的MyAsyncTask类,然后调用

MyAsyncTask asynct = new MyAsyncTask(task);
2asynct.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
代码:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
02private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
03private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
04private static final int KEEP_ALIVE = 1;
0506....
07/**
08* An {@link Executor} that can be used to execute tasks in parallel.
09*/
10public static final Executor THREAD_POOL_EXECUTOR
11= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
12TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

它的几个参数CORE_POOL_SIZE, MAXIMUN_POOL_SIZE, 都是根据当前手机的处理器数量进行动态定义的。
那么,继续往下面看,看这几个参数传进去后是什么意思。
2.    代码路径
\libcore\luni\src\main\java\java\util\concurrent\ThreadPoolExecutor.java
代码:

public ThreadPoolExecutor(int corePoolSize,
02int maximumPoolSize,
03long keepAliveTime,
04TimeUnit unit,
05BlockingQueue<Runnable> workQueue,
06ThreadFactory threadFactory,
07RejectedExecutionHandler handler) {
08if (corePoolSize < 0 ||
09maximumPoolSize <= 0 ||
10maximumPoolSize < corePoolSize ||
11keepAliveTime < 0)
12throw new IllegalArgumentException();
13if (workQueue == null || threadFactory == null || handler == null)
14throw new NullPointerException();
15this.corePoolSize = corePoolSize;
16this.maximumPoolSize = maximumPoolSize;
17this.workQueue = workQueue;
18this.keepAliveTime = unit.toNanos(keepAliveTime);
19this.threadFactory = threadFactory;
20this.handler = handler;
21}
2223/**
24* The default rejected execution handler
25*/
26private static final RejectedExecutionHandler defaultHandler =
27new 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两种线程池分析和总结相关推荐

  1. Android AsyncTask两种线程池分析和总结

    转自:http://bbs.51cto.com/thread-1114378-1-1.html Android AsyncTask两种线程池分析和总结 (一)    前言 在android Async ...

  2. android asynctask源码分析,Android通过Handler与AsyncTask两种方式动态更新ListView(附源码)...

    本文实例讲述了Android通过Handler与AsyncTask两种方式动态更新ListView的方法.分享给大家供大家参考,具体如下: 有时候我们需要修改已经生成的列表,添加或者修改数据,noti ...

  3. Android中Callable、Future、FutureTask的概念以及几种线程池的使用

    学习线程池必备知识: 在开始介绍线程池之前,先来介绍下Callable和Future的概念,众所周知,Android中实现多线程的方式有两种,实现Runnable接口或者继承一个Thread,但是这两 ...

  4. Java 四种线程池的用法分析

    1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() {@Overridepublic void run() {// ...

  5. Android开发——Android中常见的4种线程池(保证你能看懂并理解)

    0.前言 转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52415337 使用线程池可以给我们带来很多好处,首先通过线程池中线程的重用 ...

  6. android AsyncTask 只能在线程池里单个运行的问题

    android 的AysncTask直接调用Execute会在在一个线程池里按调用的先后顺序依次执行. 如果应用的所有网络获取都依赖这个来做,当有一个网络请求柱塞,就导致其它请求也柱塞了. 在3.0 ...

  7. java线程池饱和策略_干货:Java几种线程池的分析和使用。

    原标题:干货:Java几种线程池的分析和使用. 利用线程池的优势: 1.降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 2.提高响应速度.当任务到达时,任务可以不需要等到线程创建 ...

  8. Android性能优化之使用线程池处理异步任务

    说到线程,我想大家都不陌生,因为在开发时候或多或少都会用到线程,而通常创建线程有两种方式: 1.继承Thread类 2.实现Runnable接口 虽说这两种方式都可以创建出一个线程,不过它们之间还是有 ...

  9. [转]new Thread的弊端及Java四种线程池的使用

    介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端 执行一个异步任务你还只是如下new ...

最新文章

  1. C++ while(cin>>a) cin输入直到回车结束
  2. MATLAB的xlsread无法读入数据问题
  3. ConcurrentHashMap,一个更快的HashMap
  4. 检查密码 (15 分)
  5. NETSCREEN用L2TP方式建立×××
  6. 2020 有哪些不容错过的前端技术趋势? | 凌云时刻
  7. 基于RF框架的接口测试之代码分层管理
  8. 【无限互联】SDWebImage图片缓存流程分析
  9. 为基于GTK/CLUTTER的库增加GJS支持
  10. 只有1kb的清理软件_1kb文件夹快捷方式病毒专杀工具下载
  11. PPP和PPPOE详解
  12. Flutter基础七:Sliver,设置一些样式
  13. 尊享e生爸妈版2020:60岁以上的老人也能买百万医疗险了?!
  14. java基础知识总结(三)
  15. 微星z370安装linux系统,在MSI z370主板上安装win7和BIOS设置的详细教程
  16. 说话人识别matlab实现
  17. 普林斯顿大学的计算机学课的作业
  18. linux访问网页详细过程
  19. 选购手机时,运行内存和处理器先考虑哪个?来说说你的观点
  20. m2cgen库:将Python(sklearn)训练的模型转化为C/C++代码

热门文章

  1. 使用 Blazor 开发内部后台(二):了解 Blazor 组件
  2. .Net Core with 微服务 - Seq 日志聚合
  3. NET问答: 如何从 event 中移除所有的 handler ?
  4. ASP.NET Core 对Controller进行单元测试
  5. 初识ABP vNext(12):模块的独立运行与托管
  6. 跟我一起学Redis之看完这篇比常人多会三种类型实战(又搞了几个小时)
  7. 用.NetCore 编译国产老牌PHP论坛DiscuzX ,世界上最好的语言从此属于.Net 的一员
  8. 在.NET中使用DiagnosticSource
  9. .NET Core 下的爬虫利器
  10. 微软发布 VS Code Java 安装程序,一键安装所有 Java 开发环境