0. 相关资料
源码地址
android.os.AsyncTask
java.util.concurrent.Executors
参考文章
Android实战技巧:深入解析AsyncTask
ThreadPoolExecutor Java文档
Executors Java文档
java中关键字volatile的作用
1. AsyncTask#execute与AsyncTask#executeOnExecutor
public static void execute(Runnable runnable)
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params)
AsyncTask#execute本质是调用THREAD_POOL_EXECUTOR,通过synchronized来保证线程池中的任务是一个一个调用的。
AsyncTask#executeOnExecutor可以指定一个Execurtor来进行任务调度。
AsyncTask拥有2个内置Executor,Executors拥有4个内置的Executor,这6个Executor都可以在AsyncTask#executeOnExecutor上面使用。一个Executor最多能执行多少个任务?这些Executor有什么区别?如何自定义一个Executor?这些问题是本文关注的重点。
2. ThreadPoolExecutor的构造
上面说的六个Executor都是ThreadPoolExecutor,区别是构造对象的时候,使用的参数不同。下面说明ThreadPoolExecutor的构造函数。
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor Java文档的描述如下:
corePoolSize - 池中所保存的线程数,包括空闲线程。
maximumPoolSize - 池中允许的最大线程数。
keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。
unit - keepAliveTime 参数的时间单位。
workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute 方法提交的 Runnable 任务。
另外文档中描述了corePoolSize与maximumPoolSize的关系:如果运行的线程多于 corePoolSize 而少于 maximumPoolSize,则仅当队列满时才创建新线程。
我的理解是这样的:
如果运行的任务数量小于corePoolSize ,那么新的任务会立即执行。
如果任务数量大于了corePoolSize ,那么会放到workQueue中,如果workQueue满了,但是任务数量没有超过maximumPoolSize ,那么创建一个新线程来执行。
如果workQueue满了,池中任务数量已经达到maximumPoolSize,那么会执行ThreadPoolExecutor的错误处理机制,默认是丢弃该任务。
3. AsyncTask内置Executor
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
SERIAL_EXECUTOR实际上调用THREAD_POOL_EXECUTOR,只是通过synchronized来保证线程池中的任务是一个一个调用的。AsyncTask#execute就是使用SERIAL_EXECUTOR,因此AsyncTask#execute在执行的时候是一个一个执行的。注意SERIAL_EXECUTOR 是静态的,所以无论在哪个线程中,使用AsyncTask#execute执行任务,任务都在一起执行。
THREAD_POOL_EXECUTOR,corePoolSize=5,maximumPoolSize=128,keepAliveTime =1,keepAliveTime =秒,workQueue= LinkedBlockingQueue<Runnable>(10)
可以有5个线程可以同时运行
超过5个之后的线程会放到workQueue
队列中最多放10个,超过之后,直接创建新的线程直接执行
最多一共能放128个线程,超过128个之后的线程会被丢弃
4. Executors的内置Executor
Executors#newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
SynchronousQueue是不存储元素的阻塞队列。由于corePoolSize=0,每次新来一个任务,都会放到workQueue中,但是SynchronousQueue是不存储元素的,所以会立即开启线程执行。由于maximumPoolSize=Integer.MAX_VALUE,执行的线程数量是没有上限的。文档中说,
对于执行很多短期异步任务的程序而言,这些线程池通常可提高程序性能。
Executors#newFixedThreadPool()
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
池中最多可以同时运行nThreads个线程,多余的任务都放在workQueue中,workQueue没有上限,可以存储任意多个任务。
Executors#newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
池中只能运行一个线程,其他任务都放在workQueue中。相当于Executors#newFixedThreadPool(1)。
Executors#newSingleThreadScheduledExecutor
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
这里主要关注两个Executor,Executors#newCachedThreadPool()和Executors#newFixedThreadPool()。