FutureTask 源码分析

介绍

FutureTask 是一个可以取消的异步任务。它的计算通过 Callable 来实现,使用 FutrueTask 的优势在于:

  1. 可以获取线程执行后的返回结果
  2. 提供了超时控制功能

同时实现了 Runnable 以及 Future 接口

源码分析

FutureTask 的状态

NEW、COMPLETING、NORMAL、EXCEPTIONAL、CANCELLED、INTERRUPTING、INTERUPTED

FutureTask 内部结构

其中 outcome 是结果(包括正确执行的结果、异常、cancel 等),runner 为执行 callable 任务的线程、waiters可以理解过一个 stack,因为在调用 get 方法的线程放入 waiters 中。

public class FutureTask<V> implements RunnableFuture<V> {
    
    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;
    /** The underlying callable; nulled out after running */
    private Callable<V> callable;
    /** The result to return or exception to throw from get() */
    private Object outcome; // non-volatile, protected by state reads/writes
    /** 执行callable的线程 **/
    private volatile Thread runner;
    /**
     * Treiber stack of waiting threads
     * 使用Treiber算法实现的无阻塞的Stack,
     * 用于存放等待的线程
     */
    private volatile WaitNode waiters;
    
    static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }
    
    public V get() throws InterruptedException, ExecutionException {
        ...
    }
    
    public V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException {
        ...
    }

FutureTask 的构造方法

有两个构造方法,一个是为 Callable 类型的入参,一个是传入一个 Runnable 对象以及 Result 结果对象,因为 Runnable 对象没有返回值,所以需要通过结果对象获取返回值,本质也是将这两个参数通过包装类实现 Callable 的接口。

  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;       
    }

run 方法

因为 FutureTask 本身实现了 Runnable 接口,因此需要实现 run 方法,在该类的 run 方法里面,本质是去执行了 callable 的 call 方法。

run 方法执行的过程:

  1. 只有 state 为 NEW 的时候才执行任务
  2. 执行前设置 runner 为当前线程、使用 CAS 设置是为了防止竞争
  3. 然后执行 Callable 对象的 call 方法
  4. 如果执行任务的过程中发生异常,捕获并设置异常

set 方法

设置返回结果,现将状态改为 COMPLETEING,然后在设置微 NORMAL

protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion();
    }
}

finishCompletion方法

由于任务的执行是异步的,因此其他线程通过 get 方法去获取返回值的时候可能此时结果还没有计算完成,会被阻塞加入到 WaitNode 节点中。当set 方法设置返回结果后,需要唤醒在等待结果的线程,获取返回值。

private void finishCompletion() {
    // assert state > COMPLETING;
    // 执行该方法时state必须大于COMPLETING
    // 逐个唤醒waiters中的线程
    for (WaitNode q; (q = waiters) != null;) {
        // 设置栈顶节点为null
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            for (;;) {
                Thread t = q.thread;
                // 唤醒线程
                if (t != null) {
                    q.thread = null;
                    LockSupport.unpark(t);
                }
                // 如果next为空,说明栈空了,跳出循环
                WaitNode next = q.next;
                if (next == null)
                    break;
                // 方便gc回收
                q.next = null; // unlink to help gc
                // 重新设置栈顶node
                q = next;
            }
            break;
        }
    }
    // 钩子方法
    done();
    callable = null;        // to reduce footprint
}

get 方法

有两个方法,第二个方法提供了超时控制,如果在规定时间内任务还未执行完毕或者状态还是 COMPLETEING,则抛出 TimeoutException。第一个方法会一直阻塞直到获取结果。

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);
}

awaitDone方法

awaitDone方法的工作是根据状态来判断是否能够返回结果,如果任务还未执行完毕,要添加到waiters中并阻塞,否则返回状态。

其中阻塞到期时间用的方法为:LockSupport.parkNanos(this, nanos); 没有阻塞的方法为: LockSupport.park(this);

removeWaiter方法

这个方法在 awaitDone 方法被调用,主要用于处理中断或者到期的 get() 请求。通过将当前节点的 thread 设置为 null,然后遍历栈,找到该节点并移除。

private void removeWaiter(WaitNode node) {
    if (node != null) {
        // 将thread设置为null是因为下面要根据thread是否为null判断是否要把node移出
        node.thread = null;
        // 这里自旋保证删除成功
        retry:
        for (;;) {          // restart on removeWaiter race
            for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                s = q.next;
                // q.thread != null说明该q节点不需要移除
                if (q.thread != null)
                    pred = q;
                // 如果q.thread == null,且pred != null,需要删除q节点
                else if (pred != null) {
                    // 删除q节点
                    pred.next = s;
                    // pred.thread == null时说明在并发情况下被其他线程修改了;
                    // 返回第一个for循环重试
                    if (pred.thread == null) // check for race
                        continue retry;
                }
                // 如果q.thread != null且pred == null,说明q是栈顶节点
                // 设置栈顶元素为s节点,如果失败则返回重试
                else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                      q, s))
                    continue retry;
            }
            break;
        }
    }
}

cancel方法

总得来说任务一但开始执行,那么久不允许取消,返回 false。

public boolean cancel(boolean mayInterruptIfRunning) {
        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 {
            finishCompletion();
        }
        return true;
    }

report方法

get方法在调用awaitDone方法后,会调用report方法进行返回。

根据状态判断,有三种返回值的情况:

  • 如果状态为 s == NORMAL 表示 outcome 为正常返回的结果
  • 如果状态 s >= CANCELED 表示任务被取消了,因此抛出CancellationException
  • 如果状态 s < CANCELED 状态只能是 EXCEPTIONAL,表示任务在执行过程中出现了异常,此时 outcome 为异常结果,因此抛出ExecutionException
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);
}

总结

posted @ 2024-01-16 21:24  fisher1994  阅读(8)  评论(0)    收藏  举报