转自: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 ; |
03 | for ( int i = 1 ; i <= produceTaskMaxNumber; i++){ |
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 ); |
12 | static class MyAsyncTask extends AsyncTask<Integer, Integer, Integer>{ |
13 | private static int consumeTaskSleepTime = 2000 ; |
15 | private Object threadPoolTaskData; |
16 | public MyAsyncTask(String s){ |
17 | threadPoolTaskData = s; |
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()); |
26 | Thread.sleep(consumeTaskSleepTime); |
29 | Log.d( "Sandy" , "" , e); |
31 | threadPoolTaskData = null ; |
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 ; |
08 |
* An {@link Executor} that can be used to execute tasks in parallel. |
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, |
05 | BlockingQueue<Runnable> workQueue, |
06 | ThreadFactory threadFactory, |
07 | RejectedExecutionHandler handler) { |
08 | if (corePoolSize < 0 || |
09 | maximumPoolSize <= 0 || |
10 | maximumPoolSize < corePoolSize || |
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; |
24 |
* The default rejected execution handler |
26 | private static final RejectedExecutionHandler defaultHandler = |
这是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.执行流程 代码路径: frameworks\base\core\java\android\os\AsyncTask.java 代码:
01 | public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, |
04 | exec.execute(mFuture); |
08 | private static class SerialExecutor implements Executor { |
09 | final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>(); |
11 | public synchronized void execute( final Runnable r) { |
12 | mTasks.offer( new Runnable() { |
21 | if (mActive == null ) { |
26 | protected synchronized void scheduleNext() { |
27 | if ((mActive = mTasks.poll()) != null ) { |
28 | THREAD_POOL_EXECUTOR.execute(mActive); |
嗯,它会调用到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触屏监听的一些想法
- android:Handler整理总结
- Android网络应用(图片查看器)
- 语音识别(加注释)
- Android:网络:多线程断点下载
- Android(安卓)A/B system - update_engine
- Android(安卓)Gitlab CI/CD脚本
- Android(安卓)第三方开源:Volley通过网络下载数据
- 一个android 异步多线程类介绍
随机推荐
-
『转』Android(安卓)推送方式
-
Android(安卓)so文件浅析
-
android中动态实现spinner下拉菜单
-
C#:Android的未来?
-
关于AndroidManifest.xml中activity标签
-
Grnymotion模拟器和Android真机访问PC端T
-
android 之 长度单位
-
android图像处理系列之七--图片涂鸦,水印-图
-
Android(安卓)Layout 之 RelativeLayout
-
ADB基本命令