从Java SE 5.0开始引入了Callable和Future,通过它们构建的线程,在任务执行完成后就可以获取执行结果.
FutureTask可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable或者Callable的任务给FutureTask,直接调用其run方法或者放入线程池执行,之后可以在外部通过FutureTask的get方法异步获取执行结果,因此,FutureTask非常适合用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。另外,FutureTask还可以确保即使调用了多次run方法,它都只会执行一次Runnable或者Callable任务,或者通过cancel取消FutureTask的执行等。
//Runnable封装一个异步运行的任务,可以把它想象成为一个没有参数和返回值的异步方法。
public interface Runnable {
public abstract void run();
}
//Callable与Runnable类似,但是有返回值。Callable接口是一个参数化的类型,只有一个方法call。
//类型参数是返回值的类型。例如,
// Callable<Integer>表示一个最终返回Integer对象的异步计算。
public interface Callable<V> {
V call() throws Exception;
}
//Future保存异步计算的结果。可以启动一个计算,将Future对象交给某个线程,然后忘掉它。
//Future对象的所有者在结果计算好之后就可以获得它。
//如果第一个get方法的调用被阻塞,直到计算完成。
//如果在计算完成之前,第二个get方法的调用超时,抛出一个TimeoutException异常。
//如果运行该计算的线程被中断,两个方法都将抛出InterruptedException。
//如果计算已经完成,那么get方法立即返回。
//如果计算还在进行,isDone方法返回false;如果完成了,则返回true。
//可以用cancel方法取消该计算。如果计算还没有开始,它被取消且不再开始。
//如果计算处于运行之中,那么如果mayInterrupt参数为true,它就被中断。
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;
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
//FutureTask包装器是一种非常便利的机制,同时实现了Future和Runnable接口。
public class FutureTask<V> implements RunnableFuture<V> {
}
FutureTask
FutureTask除了实现了Future接口外还实现了Runnable接口,因此FutureTask也可以直接提交给Executor执行。 当然也可以调用线程直接执行(FutureTask.run())。接下来我们根据FutureTask.run()的执行时机来分析其所处的3种状态:
(1)未启动,FutureTask.run()方法还没有被执行之前,FutureTask处于未启动状态,当创建一个FutureTask,而且没有执行FutureTask.run()方法前,这个FutureTask也处于未启动状态。
(2)已启动,FutureTask.run()被执行的过程中,FutureTask处于已启动状态。
(3)已完成,FutureTask.run()方法执行完正常结束,或者被取消或者抛出异常而结束,FutureTask都处于完成状态。
FutureTask的方法执行示意图
分析:
(1)当FutureTask处于未启动或已启动状态时,如果此时我们执行FutureTask.get()方法将导致调用线程阻塞;当FutureTask处于已完成状态时,执行FutureTask.get()方法将导致调用线程立即返回结果或者抛出异常。
(2)当FutureTask处于未启动状态时,执行FutureTask.cancel()方法将导致此任务永远不会执行。
当FutureTask处于已启动状态时,执行cancel(true)方法将以中断执行此任务线程的方式来试图停止任务,如果任务取消成功,cancel(…)返回true;但如果执行cancel(false)方法将不会对正在执行的任务线程产生影响(让线程正常执行到完成),此时cancel(…)返回false。
当任务已经完成,执行cancel(…)方法将返回false。
FutureTask的执行过程
1、创建一个futureTask对象task
2、提交task到调度器executor等待调度或者在另外一个线程中执行task
3、等待调度中…
如果此时currentThread调取执行结果task.get(),会有几种情况
if task 还没有被executor调度或正在执行中
阻塞当前线程,并加入到一个阻塞链表中waitNode
else if task 被其它Thread取消,并取消成功 或task处于中断状态
throw exception
else if task 执行完毕,返回执行结果,或执行存在异常,返回异常信息
如果此时有另外一个线程调用task.get(),执行过程同上
应用场景
- Future用于异步获取执行结果或者取消任务。
- 在高并发场景下确保任务只执行一次。
基本例子
Callable<Integer> myComputation = ...;
FutureTask<Integer> task = new FutureTask<Integer>(myComputation);
Thread t = new Thread(task);
t.start();
//...
Integer result = task.get(); //获取结果
参考:
Java多线程编程:Callable、Future和FutureTask浅析(多线程编程之四)
源码探索系列15—那个AsyncTask里面的FutureTask
AsyncTask
AsyncTask是FutureTask的一个扩展。实现流程:其构造方法中,定义了一个FutureTask以及一个Callable对象,之后执行execute()方法,在execute方法中首先调用的就是onPreExecute(),随后就执行Callable对象的call方法
doInBackground调用了,但是此时仔细想想还是在子线程中的,随后跟进postResult方法中发现handler,那就清晰了,doInBackground中的返回值(包括中间的进度更新)都会发送消息给主线程。这就是基本的实现流程,当然这里是往简单了说,不要忘记还有多个AsyncTask一起被启动的情况,不过原理也就是用了队列的方式调用了Callable对象的call方法,看到这里大家也会知道,问什么3.0之后开启多个AsyncTask的时候,它是同步执行的,也就是执行了一个之后再去执行下一个,因为它是队列的调度,不断的next.
参考:
android之Futuretask、Timer、AsyncTask的使用及原理解析
用法:
//ExecutorService
try {
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(new Callable<String>() {
@Override
public String call() throws Exception {
return "我是ExecutorService";
}
});
String s = future.get();
System.out.println("ExecutorService , s = "+s);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
//new Thread
try {
FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
return "我是new Thread";
}
});
new Thread(futureTask).start();
String s = futureTask.get();
System.out.println("new Thread , s = "+s);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}