FutureTask源码分析

264 阅读7分钟

前言

在深入分析源码之前,我们再来拎一下FutureTask到底是干嘛的。人如其名,FutureTask包含了Future和Task两部分。
FutureTask实现了RunnableFuture接口,RunnableFuture接口又继承了Runnable和Future接口,所以FutureTask既有Runnable执行任务的功能,又有Futrue获取结果的功能。
状态、队列、cas是Java并发工具中的三板斧,像AQS、Condition、FutureTask等都是基于此实现的。

状态

在FutrueTask状态是用state属性来表示的,被valatile修饰,代表修改结果对其它线程立即可见且不允许重排序。

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; // 已中断

state是贯穿整个FutureTask最核心的属性,属性的值代表不同的执行状态,随着任务的执行,状态在不断变化,FutureTask共定义了七种状态:1个初始状态,2个中间状态,3个最终状态。
虽说状态有这么多,但是状态的转换路径却只有四种: 每种状态的含义上面代码已经注释了,在此不在说明。

队列

FutureTask队列是基于Treeber stack实现的单向列表,所有等待结果的线程最后都会加入到Treeber stack中。 对Treeber stack不太熟悉的,可以看这里

 static final class WaitNode {
    volatile Thread thread; // 对应的线程
    volatile WaitNode next; // 下一个节点
    WaitNode() { thread = Thread.currentThread(); } // 设置当前执行的线程
}

在FutureTask中有一个waiters属性指向此栈的栈顶节点。

private volatile WaitNode waiters; // Treiber栈 指向栈的栈顶节点

队列结构:

5.png

源码分析

主要属性

/**
 * The run state of this task, initially NEW.  The run state
 * transitions to a terminal state only in methods set,
 * setException, and cancel.  During completion, state may take on
 * transient values of COMPLETING (while outcome is being set) or
 * INTERRUPTING (only while interrupting the runner to satisfy a
 * cancel(true)). Transitions from these intermediate to final
 * states use cheaper ordered/lazy writes because values are unique
 * and cannot be further modified.
 *
 * Possible state transitions:
 * NEW -> COMPLETING -> NORMAL
 * NEW -> COMPLETING -> EXCEPTIONAL
 * 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 Callable<V> callable; // 任务

private Object outcome; // non-volatile, protected by state reads/writes // 保存结果,正常完成或者异常

private volatile Thread runner; // 执行任务的线程

private volatile WaitNode waiters; // Treiber栈 指向栈的栈顶节点

private static final sun.misc.Unsafe UNSAFE; // 魔法类
private static final long stateOffset; // 状态偏移量
private static final long runnerOffset; // 任务执行线程偏移量
private static final long waitersOffset; // 指向栈顶节点的偏移量

主要方法

FutureTask实现了RunnableFuture,RunnableFuture又继承了Runnable和Future,所以FutureTask也间接实现了Runnable。

run

public void run() {
    if (state != NEW || // 如果state 不是初始状态或者更新当前执行线程失败,则直接返回,保证任务只能一个线程执行
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread())) // 原子更新执行任务线程
        return;
    try {
        Callable<V> c = callable; // 任务
        if (c != null && state == NEW) { // 任务不是null且执行状态是初始状态,再次说明一个任务有且只能有一个线程去执行
            V result; // 保存执行结果
            boolean ran; // 任务是否正常完成
            try {
                result = c.call(); // 执行任务的call方法
                ran = true;
            } catch (Throwable ex) { // 执行业务代码出现异常
                result = null;
                ran = false;
                setException(ex); // 设置抛出的异常
            }
            if (ran)
            /**
             * 如果在任务执行过程中,有其它线程调用了cancle(false),业务代码也会正常执行完
             * 只是在赋值执行结果的时候回赋值失败,因为state已经从“NEW”变成“CANCELLED”
             */
                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
        /**
         * 在当前线程执行run方法的同时,有可能其他线程取消了任务的执行,
         * 此时其他线程就可能对state状态进行改写,这也就是我们在设置终止状态的时候用putOrderedInt方法,
         * 而没有用CAS操作的原因——我们无法确信在设置state前是处于COMPLETING中间态还是INTERRUPTING中间态。
         */
        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(); //收尾工作,循环唤醒等待该任务结果的所有线程
    }
}

protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) { // 如果线程从“初始”状态更新为“完成中”
        outcome = t; // 赋值异常结果
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        finishCompletion(); //收尾工作,循环唤醒等待该任务结果的所有线程
    }
}

run方法内部相对还是比较简单的,简单的说下run的执行流程:

  1. 如果状态是初始状态且没有其它线程执行过
  2. 执行call方法
    2.1 正常执行结束,调用set方法,把结果赋值给outcome属性,更新state 为"NORMAL", 调用finishCompletion方法唤醒等待的线程
    2.2 执行业务代码出现异常,调用setException,把异常赋值给outcome属性,更新state 为"EXCEPTIONAL",调用finishCompletion方法唤醒等待的线程
  3. 收尾工作,等待状态变成最终状态

finishCompletion

private void finishCompletion() {
    // assert state > COMPLETING;
    for (WaitNode q; (q = waiters) != null;) { // 从栈的栈顶开始遍历
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { // 清空Treiber栈
            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
}

此方法的工作就是唤醒等待结果的线程

get

public V get() throws InterruptedException, ExecutionException {
    int s = state; // 状态
    if (s <= COMPLETING) // 如果是初始状态或者设置结果中状态,就加入队列等待
        s = awaitDone(false, 0L);
    return report(s);
}

private V report(int s) throws ExecutionException {
    Object x = outcome; // 结果
    if (s == NORMAL) // 正常结束状态
        return (V)x;
    if (s >= CANCELLED) // 如果取消或者中断,在这里不会有中间状态发生了,因为不管是正常返回结果,还是执行call出现异常或者
        // 其它线程取消了该任务,都会等待更新到最终状态才会唤醒等待结果的线程
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}

该方法其实很简单,当任务还没有执行完毕或者正在设置执行结果时,我们就使用awaitDone方法等待任务进入终止态,注意,awaitDone的返回值是任务的状态,而不是任务的结果。任务进入终止态之后,我们就根据任务的执行结果来返回计算结果或者抛出异常。
我们先来看看等待任务完成的awaitDone方法,该方法是获取任务结果最核心的方法,它完成了获取结果,挂起线程,响应中断等诸多操作:

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 如果状态是设置结果中,则放弃cpu
            Thread.yield();
        else if (q == null)
            q = new WaitNode(); // 创建等待节点
        else if (!queued)
            /*
                这里是使用的Treeber栈,把新入节点原子更新为栈顶节点
             */
            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); // 挂起线程
    }
}

cancel

/*
    取消任务:
    有几种情况不能取消
       1: 任务已经完成
       2: 任务已经取消
       3: 其它情况导致的不能取消
 */
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; // 返回true,不代表真正的中断了
}

总结

FutureTask实现了Runnable和Future接口,它表示了一个带有任务状态和任务结果的任务,它的各种操作都是围绕着任务的状态展开的,值得注意的是,在所有的7个任务状态中,只要不是NEW状态,就表示任务已经执行完毕或者不再执行了,并没有表示“任务正在执行中”的状态。

除了代表了任务的Callable对象、代表任务执行结果的outcome属性,FutureTask还包含了一个代表所有等待任务结束的线程的Treiber栈,这一点其实和各种锁的等待队列特别像,即如果拿不到锁,则当前线程就会被扔进等待队列中;这里则是如果任务还没有执行结束,则所有等待任务执行完毕的线程就会被扔进Treiber栈中,直到任务执行完毕了,才会被唤醒。

FutureTask虽然为我们提供了获取任务执行结果的途径,遗憾的是,在获取任务结果时,如果任务还没有执行完成,则当前线程会自旋或者挂起等待。

参考:blog.csdn.net/qq_42499188…
segmentfault.com/a/119000001…