详解一说明了线程池的基本配置参数,这里继续说AsyncTask内部是怎样对线程池进行配置使用的
AsyncTask对线程池的配置
以API 23(6.0)为例,看一看AsyncTask里面的线程池是以什么参数构造的;AsyncTask里面有“两个”线程池;一个THREAD_POOL_EXECUTOR
一个SERIAL_EXECUTOR
;之所以打引号,是因为其实SERIAL_EXECUTOR
也使用THREAD_POOL_EXECUTOR
实现的,只不过加了一个队列弄成了串行而已,那么这个THREAD_POOL_EXECUTOR
是如何构造的呢?
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;
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
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可以看到,AsyncTask里面线程池是一个核心线程数为CPU + 1
,最大线程数为CPU * 2 + 1
,工作队列长度为128的线程池;并且没有传递handler
参数,那么使用的就是默认的Handler(拒绝执行).
AsyncTask使用线程池易导致的问题
从AsyncTask对线程池的使用,可以看到容易出现以下问题:
- 如果任务过多,那么超过了工作队列以及线程数目的限制导致这个线程池发生阻塞,那么悲剧发生,默认的处理方式会直接抛出一个异常导致进程挂掉。假设你自己写一个异步图片加载的框架,然后用AsyncTask实现的话,当你快速滑动ListView的时候很容易发生这种异常;这也是为什么各大ImageLoader都是自己写线程池和Handlder的原因。
- 这个线程池是一个静态变量(类变量);那么在同一个进程之内,所有地方使用到的AsyncTask默认构造函数构造出来的AsyncTask都使用的是同一个线程池,如果App模块比较多并且不加控制的话,很容易满足第一条的崩溃条件;如果你不幸在不同的AsyncTask的doInBackgroud里面访问了共享资源,那么就会发生各种并发编程问题。
- 在AsyncTask全部执行完毕之后,进程中还是会常驻corePoolSize个线程;在Android 4.4 (API 19)以下,这个corePoolSize是hardcode的,数值是5;API 19改成了
cpu + 1
;也就是说,在Android 4.4以前;如果你执行了超过五个AsyncTask;然后啥也不干了,进程中还是会有5个AsyncTask线程