JDK1.5 引入了Future模式,Future代表了一个异步任务的执行结果。Future模式可以理解成:主线程将待执行的任务提交给子线程执行后,可以先获取任务结果的持有者Future。然后主线程可以去执行其他的任务。等待到要关注之前任务的执行结果时,再从Future中获取。下面是用户注册的场景。
同步实现时,注册接口响应时间150ms= 用户信息保存50ms + 发送短信50ms + 发送邮件50ms
异步实现时,注册接口响应时间100ms= 用户信息保存50ms + max(发送短信50ms, 发送邮件50ms)
Future 接口核心方法
/*尝试取消当前执行的任务*/
boolean cancel(boolean mayInterruptIfRunning);
/*判断当前执行的任务是否被取消*/
boolean isCancelled();
/*判断当前执行的任务是否完成,正常结束、发生异常或任务被取消都认为任务完成*/
boolean isDone();
/*获取当前执行任务对应的结果,将阻塞等待任务执行完*/
V get() throws InterruptedException, ExecutionException;
/*等待一段时间,获取当前执行任务对应的结果,将阻塞等待任务执行完,如果对应时间还未获取结果,
* 抛出TimeoutException
*/
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
后面所有分析全部基于JDK8
在JDK中Future接口的实现类为FutureTask,其UML图如下。
FutureTask同时实现了Runnable与Future的功能。实现Runnable主要是为了让Executor可以执行,实现Future为了能够持有执行结果的引用。
FutureTask的成员变量如下:
/*当前任务的执行状态*/
private volatile int state;
/*当前要执行的任务,执行完后将被设置为null*/
private Callable<V> callable;
/*当前任务的执行结果,当发生异常或被取消时为对应异常,非volatile,由state状态保证线程安全*/
private Object outcome;
/*执行当callable任务的线程引用,为当前线程通过CAS方式原子设置*/
private volatile Thread runner;
/*Treiber椎,用于保存由于调用Future.get方法而阻塞的线程*/
private volatile WaitNode waiters;
先来看一下用于维护当前任务执行状态的state成员变量。
FutureTask内部定义了,任务执行的7种状态。
//任务的初始初始化状态
private static final int NEW = 0;
//执行结果设置中时,的中间状态
private static final int COMPLETING = 1;
//任务正常执行完成的最终状态
private static final int NORMAL = 2;
//任务执行出错的最终状态
private static final int EXCEPTIONAL = 3;
//调用Future.cancell取消任务的最终状态
private static final int CANCELLED = 4;
//执行任务的线程被中断的中间状态
private static final int INTERRUPTING = 5;
//执行任务的线程被中断的最终状态
private static final int INTERRUPTED = 6;
上图展示了,Future任务状态的扭转图。new状态时通过调用set()方法、setException()方法可以使状态最终分别转变为NORMAL与EXCEPTIONAL; new状态时通过调用cancel(flase)方法、cancel(true)方法可以可以使状态最终分别转变为CANCELLED与INTERRUPTED。
再看一下用于保存由于调用Future.get方法而阻塞的线程的Treiber被引用成变量waiters。
static final class WaitNode {
volatile Thread thread;
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
每当一个线程调用Future.get去获取任务的执行结果时,如果当前任务还没有执行