这是我参与更文挑战的第10天,活动详情查看: 更文挑战
本文基于 Android 9.0.0 的源代码
framework/base/core/java/andorid/os/AsyncTask.java
简介
之前讲解了能够在后台工作线程中执行耗时任务的IntentService框架,在这里我们继续学习Android
提供的另外一个异步执行任务的框架AsyncTask
,它和IntentService
既有相似点也有不同点,其相似之处在于都能在新的线程中执行耗时任务防止阻塞主线程,不同之处在于AsyncTask
能够追踪任务的执行过程和结果并在主线程中显示出来。
知识储备
LinkedBlockingQueue 链阻塞队列
阻塞队列BlockingQueue
被广泛使用在“生产者-消费者”问题中,其原因是BlockingQueue
提供了可阻塞的插入和移除的方法。当队列容器已满,生产者线程会被阻塞,直到队列未满;当队列容器为空时,消费者线程会被阻塞,直至队列非空时为止。
LinkedBlockingQueue
类实现了 BlockingQueue
接口。LinkedBlockingQueue
内部以一个链式结构(链接节点)对其元素进行存储。如果需要的话,这一链式结构可以选择一个上限。如果没有定义上限,将使用 Integer.MAX_VALUE 作为上限。LinkedBlockingQueue
内部以 FIFO(先进先出)的顺序对元素进行存储。队列中的头元素在所有元素之中是放入时间最久的那个,而尾元素则是最短的那个。
ArrayDeque
数组队列 ArrayDeque的特点
- 大小自增长的队列
- 内部使用数组存储数据
- 线程不安全
- 内部数组长度为8、16、32….. 2的n次方
- 头指针head从内部数组的末尾开始,尾指针tail从0开始,在头部插入数据时,head减一,在尾部插入数据时,tail加一。当head==tail时说明数组的容量满足不了当前的情况,此时需要扩大容量为原来的二倍。
ExecutorService 执行器服务
java.util.concurrent.ExecutorService 接口表示一个异步执行机制,使我们能够在后台执行任务。因此一个 ExecutorService 很类似于一个线程池。
ExecutorService 简单实现
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
System.out.println("Asynchronous task");
}
});
executorService.shutdown();
首先使用 newFixedThreadPool() 工厂方法创建一个 ExecutorService
。这里创建了一个十个线程执行任务的线程池。
然后,将一个 Runnable
接口的匿名实现类传递给 execute() 方法。这将导致 ExecutorService 中的某个线程执行该 Runnable。
ThreadPoolExecutor 线程池执行者
ava.util.concurrent.ThreadPoolExecutor 是 ExecutorService
接口的一个实现。ThreadPoolExecutor
使用其内部池中的线程执行给定任务(Callable 或者 Runnable)。
构造方法:
//构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
下面看看几个参数的含义及作用
- corePoolSize — 核心线程数,即允许闲置的线程数目
- maximumPoolSize — 最大线程数,即这个线程池的容量
- keepAliveTime — 非核心线程的闲置存活时间
- unit — 上一个参数的单位
- workQueue — 任务队列(阻塞队列)
- threadFacotry — 线程创建工厂
- handler — 当线程池或者任务队列容量已满时用于 reject
Callable&&Future
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
简单来讲,Callable
接口等价于Runable
,call()等价于run(),区别在于它是有返回值的。
我们可以通过ExecutorService
调用Callable
,执行后将返回Future对象
,比如:
Future<String> future = Executors.newSingleThreadExecutor().submit(mCallable);
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
Future
接口两个方法着重理解下,一是cancel(boolean mayInterruptIfRunning)
,顾名思义就是终止线程,二是get()
,它会阻塞线程,直到Callable的call()返回对象,并以此作为返回值。至于mayInterruptIfRunning
这个boolean值的含义,大家看看FutureTask
中相应的源码就直到了,其实只是多了thread.interrupt()
的逻辑而已。结合Callable的代码,Future的使用如下:
Future<String> future = Executors.newSingleThreadExecutor().submit(mCallable);
//阻塞线程,等待Callable.call()的返回值
String result = future.get();
FutureTask
public class FutureTask<V> implements RunnableFuture<V>
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}```
从FutureTask
的继承关系来看,它既是Runable
也是Future,所以我们可以把当做Runable来使用,同时它也具备Future的能力,可以终止线程,可以阻塞线程,等待Callable的执行,并获取返回值。另外要注意的是,它的构造函数是public FutureTask(Callable callable),因此实例化FutureTask时需要Callable对象作为参数。