Future简介

1 Future接口

Future接口表示异步计算的结果,一旦计算完成,计算将不能再取消。整个Future的继承体系结构如下:

代表异步结算结果。该接口提供方法:

  • 检查计算是否完成(isDone)
  • 等待计算完成(get)
  • 检索计算结果(get)
  • 取消任务(cancel)。

cancel()方法只是试图取消任务执行。若已经被取消或计算已经完成,将不能再被取消。该方法返回后,isDone()和isCancelled()方法将会始终返回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;
}

Future接口通常配合Executor接口来实现,如下:

// 方式1:Future接口实现
Future<String> future = Executor.submit(new Callable<String>() {
        public String call() {
            return doSomething();
        }
    });
// 方式2:FutureTask实现
FutureTask<String> futureTask = new FutureTask<String>(new Callable<String>() {
        public String call() {
            return doSomething();
        }
    });
executor.execute(futureTask);

2 FutureTask

FutureTaskFuture接口的典型实现类。FutureTask是一个可取消的异步计算。其类图结构如下所示:
image

FutureTask任务的异步执行,可以使用如下2种方式执行:

  • 利用Executor#execute或 ExecutorService#submit()方式执行
  • 直接放到Thread线程里执行
    FutureTask<String> futureTask = new FutureTask<>(() -> "hello");
    Thread t = new Thread(futureTask);
    t.start();
    System.out.println(futureTask.get());
    

因为FutureTask实现了Runnable接口,因此该类天然支持异步线程执行。所有的异步执行,都是通过FutureTask#run()方法实现,run()方法执行完毕后,将结果缓存,通过调用FutureTask#get()方法获取执行的结果。

2.1 执行流程

FutureTask类的state字段用于控制异步任务的执行流,如下:

/* state字段的状态流转
NEW -> COMPLETING -> NORMAL
NEW -> COMPLETING -> EXCEPTION
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED
*/
private volatile int state;
private static final int NEW          = 0;
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 Object outcome;  // 代表执行结果
private volatile Thread runner;  // 正在执行的任务的线程
private Callable<V> callable;    // 需要执行的任务

目前state字段的数值大小有顺序规律:

  • 0~2为正常状态
  • 3为异常状态
  • 4为取消
  • 5~6为中断

注意:取消和中断,都可以认为是取消情况。可以直接跳到cancel()方法,该方法会根据入参情况,来决定最终的状态为4或6。即cancel()会导致状态为CANCELLED or INTERRUPTED。

根据字段以及构造方法可知,FutureTask是一个异步执行框架,而其内部封装了需要执行的任务callable。异步执行由FutureTask本身作为Runnable的实现类完成,在异步的Runnable#run()中会同步的调用Callable#run()方法。

2.2 等待节点

FutureTask是异步执行任务的框架,因此必然存在这种情况:当任务尚在执行过程中,其他线程尝试过去该任务的执行结果。在FutureTask中会将这些线程打包成WaitNode节点,等待执行结果:

static final class WaitNode {
    volatile Thread thread;
    volatile WaitNode next;
    WaitNode() { thread = Thread.currentThread(); }
}

即所有获取结果的线程组成单向链表。在任务执行完成后,将会将这些线程unpark()。

2.3 构造方法

由构造方法可以得知,那些"种类"的任务可以构造出FutureTask

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;
}
public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;
}

由构造方法可知,FutureTask的初始状态为NEW。

2.4 执行任务

FutureTask的核心在于任务的异步执行,而异步执行的核心在于Runnable#run()方法。该方法注意如下几点:

  • 因为可能出现异常情况,因此outcome是Object类
  • 在执行完任务只会,首先将state状态设置为COMPLETING的瞬时态
  • 最后,执行finishCompletion()进行完成处理
public void run() {
    // 判断task的状态流转,并使用CAS方式以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 {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);      // 任务执行过程中出现异常
            }
            if (ran) { // 若任务正常执行结束
            set(result); 
            }  
        }
    } finally {
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}
// 任务正常结束
protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion();  // 任务正常执行完成,则upark()所有等待获取结果的线程
    }
}
// 任务异常终止
protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = t;
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        finishCompletion();
    }
}

在执行完毕后finishCompletion()方法将会unpark()所有获取结果的线程:

// 完成处理的核心在于:
// 1. 循环移除所有的WaitNode节点,并LockSupport.unpark(t)该节点上记录的线程
// 2. 调用done()方法,该方法是预留方法,用于实现类扩展。具体可以参考ExecutorCompeletionService类
private void finishCompletion() {
    // assert state > COMPLETING;  即此方法state 一定大于 COMPLETING,即任务已经是完成
    for (WaitNode q; (q = waiters) != null;) {
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            for (;;) {  // 移除并且unpark()所有等待的线程
                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
}

2.5 获取结果

get()方法由重载方法,其核心在于:若计算尚未完成,则进行等待:创建WaitNode节点进入等待队列中。若最后完成,则调用report()方法获取结果:

  • state == NORMAL正常结束,返回结果;
  • state >= CANCELLED,取消结束,抛出CancellationException;
  • state 为其余任何情况,抛出执行异常
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L);
    return report(s);
}
public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
    if (unit == null)
        throw new NullPointerException();
    int s = state;
    if (s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
        throw new TimeoutException();
    return report(s);
}
//// report()方法比较简单,先看此方法
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);
}

所以获取结果的方法核心在于awaitDone()方法

/// 3种结果:完成、中断、超时
private int awaitDone(boolean timed, long nanos) throws InterruptedException {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    for (;;) {   // 自旋操作,只有被中断抛出异常,或者state>COMPLETING时,才会跳出方法
        if (Thread.interrupted()) { // 清空等待队列,然后抛出异常以响应等待中的线程
            removeWaiter(q);        // 该方法只会清空等待队列
            throw new InterruptedException();
        }

        int s = state;
        if (s > COMPLETING) {  // 表明任务已经结束,返回数据
            if (q != null)     // 如果已经创建了WaitNode节点,则重新将其清空(等待队列中的数据,会在任务完成后的finishCompletion()方法中清空)
                q.thread = null;
            return s;
        } else if (s == COMPLETING) {  // COMPLETING表明任务即将完成,无需park(),只需要yield()然后继续尝试
            Thread.yield(); 
        } else if (q == null) {    // 如果任务一直处于NEW状态,则第1次循环执行到这里,为q创建一个WaitNode()对象
            q = new WaitNode();
        } else if (!queued) {      // 如果任务一直处于NEW状态,则第2次循环执行到这里,将q塞入等待队列中
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
        } else if (timed) {        // 如果任务一直处于NEW状态,则第3次循环执行到此处或最后的else,将当前线程parkNanos或park()
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos);
        } else {
            LockSupport.park(this);
        }
    }
}

2.6 取消任务

任务在执行期间可以被取消或被中断。取消方法会将状态从:

  • 若被取消则:NEW -> CANCELLED
  • 若被中断则:NEW -> INTERRUPTING -> INTERRUPTED。

中断or取消,取决于cancel()的入参mayInterruptIfRunning,为true则中断;否则将取消。
注意,状态由 INTERRUPTING -> INTERRUPTED之间,存在时间差,先设置为INTERRUPTING状态后,将线程进行中断,然后设置为INTERRUPTED。由于时间差较短,因此常用Thread.yield()方法让出线程进行等待

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW & UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        // 取消后,需要unpark()所有在等待的线程
        finishCompletion();
    }
    return true;
}
posted @ 2019-03-07 09:11  wolf_w  阅读(208)  评论(0编辑  收藏  举报