从字面上来看,Future表示将来的意思,也就是说现在执行某个任务可能是不能立即拿到返回值,在将来的某个时间点 当task执行完毕也就可以拿到了;那么这个返回值在哪呢?就封装在future中(实现类)
Future 最主要的作用是,比如当做一定运算的时候,运算过程可能比较耗时,有时会去查数据库,或是繁重的计算,比如压缩、加密等,在这种情况下,如果我们一直在原地等待方法返回,显然是不明智的,整体程序的运行效率会大大降低。我们可以把运算的过程放到子线程去执行,再通过 Future 去控制子线程执行的计算过程,最后获取到计算结果。这样一来就可以把整个程序的运行效率提高,是一种异步的思想
FutureTask是Future的一个实现,结合Callable并将任务执行的返回值封装在FutureTask中
FutureTask可以用做闭锁。(FutureTask实现了Future语义,表示一种抽象的可生成结果的计算)。
FutureTask表示的计算通过Callable来实现,相当于一种可生成结果的Runnable,可以处于以下3中状态
Future.get的行为取决于任务的状态,如果任务已经完成,那么get会立即返回结果,否则get将阻塞直到任务进入完成状态,然后返回结果或者抛出异常;
FutureTask实现
FutureTask维护的一些额外的状态变量,用来保存计算结果或者抛出的异常,此外还维护了一个引用,指向正在执行计算任务的线程(如果它当前处于运行状态),
因而如果任务被取消,该线程就会中断
闭锁是一种同步工具类,可以延迟线程的进度直到其到达终止状态,闭锁的作用相当于一扇门
Future.get的闭锁语义:如果发生了某个事件(由FutureTask表示的任务执行完成或者被取消),那么线程就可以恢复执行,
否则这些线程将停留在队列中并直到该事件发生
来看一个例子:
public class OneFuture {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
Future<Integer> future = service.submit(new CallableTask());
try {
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
static class CallableTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt();
}
}
}
通过submit提交callable或者runnable都可以得到future,不同的是runnable的future返回值是空的
JDK1.8
在ThreadPoolExecutor源码分析中,未分析父类AbstractExecutorService相关实现(比如我们常见的submit方法)
这里来看看父类中submit方法
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
可以看到submit方法返回了Future类型的参数,Future是一个接口,这里使用的是实现类FutureTask,封装了如何从future中取值(简单的理解就是,当某个线程执行的task,
如果已经结束并且将结果封装在了future,那我这会直接从future取就可以了;如果task未执行完毕,此时future中肯定没有执行结果,但是这个时候又想要取到该结果,那就只有通过阻塞的方式来等待task的执行完毕)
通过newTaskFor返回的就是FutureTask实例,看看FutureTask的实现关系
可以看到FutureTask最终实现了Future和Runnable接口,同时内部组合了Callable接口作为属性字段,
我们知道execute的参数是Runnable类型,因此可以使用execute来执行futureTask任务,
我们只需要在run方法中调用callable.call接口来执行task并得到结果,然后将结果封装在future即可
细心的你可能已经注意到了,submit的参数有两种类型,分别是Runnable和Callable;Callable就不说了本身就会返回结果,
Runnable是不会返回结果,FutureTask是如何如果给属性字段callable赋值,又如何通过callable.call接口来执行task并得到结果呢?来看看FutureTask的一个构造方法
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
可以看到最终是通过实现了Callable接口的适配器RunnableAdapter来封装Runnable的task和result,
这样当调用callable.call接口来执行task并得到结果时,实际便会执行task.run(), 并返回指定的result(上面的例子result传入的是null, 也就是没有返回值)
来看看FutureTask的基本参数
public class FutureTask<V> implements RunnableFuture<V> {
// 状态
private volatile int state;
// 初始状态
private static final int NEW = 0;
// 已经拿到结果,处于封装赋值futureTask阶段
private static final int COMPLETING = 1;
// 正常已完成阶段
private static final int NORMAL = 2;
// 异常完成阶段
private static final int EXCEPTIONAL = 3;
// 取消阶段
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
private Callable<V> callable;
// 处理的结果
private Object outcome; // non-volatile, protected by state reads/writes
// 运行callable的线程,在run方法中会被初始化
private volatile Thread runner;
// 在future.get上等待结果的线程列表
private volatile WaitNode waiters;
public void run() {
// 只有state=NEW状态才能向下执行
// 同时将当前执行task的线程赋值给runner
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
// 执行callable.call方法,并得到结果
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
// 设置异常状态,并改变FutureTask状态
setException(ex);
}
if (ran)
// 设置结果,并改变FutureTask状态
set(result);
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
//
这个方法也就是重写Runnable接口中的run方法,线程执行task的入口方法,如果在执行task之前被取消或者中断了,task将不会在执行了;
执行task实际就是调用callable.call来处理并得到结果,然后将结果封装在futureTask中。
将异常状态封装在futureTask中
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
// 将结果赋值给outcome
outcome = t;
// 将最终状态改为EXCEPTIONAL,也是完成状态
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
// 完成后的清理操作
finishCompletion();
}
}
通过原子操作先将状态改为COMPLETING这个临时状态(多线程情况下这里只有一个线程能操作成功,保证来了线程安全性)
已经得到任务的结果,将其封装在futureTask中
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
是不是看起来和setException操作一模一样,唯一的差别就是将最终状态改为了NORMAL
public V get() throws InterruptedException, ExecutionException {
int s = state;
// 如果未运行或者完成中状态(已经取到结果,处于最后赋值阶段),就尝试阻塞等待
if (s <= COMPLETING)
// 阻塞等待
s = awaitDone(false, 0L);
// 根据最终的状态来判断 返回结果
return report(s);
}
获取结果时会遇到这几种情况:
// timed表示是否有超时限制
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
// 如果当前线程已经被中断了,就从等待列表中移除此线程节点,并响应中断
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
// 说明已经是完成状态了(正常或者异常状态)
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued) // 如果等待列表中没有此线程节点,就添加到队尾
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
// 有超时限制的阻塞,超时时间到了将会被唤醒
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
// 直接阻塞
LockSupport.park(this);
}
}
//
主要操作
当任务执行结束后返回结果或者抛出异常
private V report(int s) throws ExecutionException {
Object x = outcome;
// 正常情况
if (s == NORMAL)
return (V)x;
// 异常情况
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
取消当前任务的执行
// 根据参数mayInterruptIfRunning来判断是应该中断在运行中的task
public boolean cancel(boolean mayInterruptIfRunning) {
// 只能取消或者中断state=NEW的情况
// 这里INTERRUPTING也是个原子性保证,在多线程环境下只有一个线程可以通过CAS操作成功,也就是只有一个线程会执行后面的逻辑
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
// 打上中断标志
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
// remove所有waitNode节点并唤醒所有等待结果的线程
finishCompletion();
}
return true;
}
先来看看参数mayInterruptIfRunning的使用,如果在task执行之前成功的CANCELLED或者INTERRUPTED,那么task将不会执行,从效果上来看也就没啥区别;
如果当task已经开始执行了,这个参数就有区别了:
从取消流程上来看:
remove所有waitNode节点并唤醒所有等待结果的线程
private void finishCompletion() {
// assert state > COMPLETING;
// 等待队列中存在等待结果的线程节点
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
// 唤醒
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
// 钩子方法,由子类实现
done();
callable = null; // to reduce footprint
}
以上流程简单来说就是情况等待队列中所有的WaitNode,并唤醒这些等待结果的线程
此方法作用是用于多次执行task,无需记录task返回的结果
protected boolean runAndReset() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return false;
boolean ran = false;
int s = state;
try {
Callable<V> c = callable;
if (c != null && s == NEW) {
try {
c.call(); // don't set result
ran = true;
} catch (Throwable ex) {
setException(ex);
}
}
} finally {
// runner must be non-null until state is settled to
// prevent concurrent calls to run()
runner = null;
// state must be re-read after nulling runner to prevent
// leaked interrupts
s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
return ran && s == NEW;
}
Runnable
public interface Runnable {
public abstract void run();
}
Callable
public interface Callable {
V call() throws Exception;
}