FutureTask的源码分析

FutureTask 实现了 RunnableFuture 这个接口,在run()方法中, 获取任务执行的结果,记录在outcome 字段中。它定义了一个state变量,记录了线程池异步任务执行的状态。通过判断任务状态和比较任务状态,来设置任务运行的结果和异常信息。它把state设置为volatile变量,可以保证它的可见性。

public class FutureTask<V> implements RunnableFuture<V> 
    /** 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
    /** The thread running the callable; CASed during run() */
    private volatile Thread runner;
    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;

在构造方法中,初始化Callable对象, 并且把状态设置为NEW.

   /**
     * Creates a {@code FutureTask} that will, upon running, execute the
     * given {@code Callable}.
     *
     * @param  callable the callable task
     * @throws NullPointerException if the callable is null
     */
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

它的任务状态一共有七种。

    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;

那么,它的状态变化又有哪些可能性呢?主要包含四种情况

* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED

下面来看下它的几个重要的方法逻辑

1. run()

public void run() {
        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 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);
        }
 }

 在这个方法里面, 先去判断当前任务的状态是否为空和是否为new, 如果是空或者不是处于新建的状态,则直接退出run方法。

接下来,通过Callable的call()方法,获取到异步任务的执行结果。如果正常,则设置结果。否则设置异常信息。在finally中,要把正在运行callable的线程设置为空,避免有多个线程同时执行call()方法。然后重新判断状态是否处于中断,如果是,则处理中断异常。

它的 set(result) 的逻辑是怎样的呢?我们来看下。

2. set(V v) 

   /**
     * Sets the result of this future to the given value unless
     * this future has already been set or has been cancelled.
     *
     * <p>This method is invoked internally by the {@link #run} method
     * upon successful completion of the computation.
     *
     * @param v the value
     */
    protected void set(V v) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = v;
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
            finishCompletion();
        }
    }

其中stateOffset 是定义当前的状态,利用UNSAFE的方法来进行比较并交换。这样做可以保证线程安全,并且不用加锁。

   // Unsafe mechanics
    private static final sun.misc.Unsafe UNSAFE;
    private static final long stateOffset;
    private static final long runnerOffset;
    private static final long waitersOffset;
    static {
        try {
            UNSAFE = sun.misc.Unsafe.getUnsafe();
            Class<?> k = FutureTask.class;
            stateOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("state"));
            runnerOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("runner"));
            waitersOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("waiters"));
        } catch (Exception e) {
            throw new Error(e);
        }
    }

把任务状态设置为COMPLETING, 然后把 结果 赋值给 Object 对象 outcome,然后再把 任务状态设置为NORMAL, 表示该线程异步任务已经正常完成。在finishCompletion方法中,唤醒其他等待执行该任务的线程,最后把callable对象设置为null。

   /**
     * Removes and signals all waiting threads, invokes done(), and
     * nulls out callable.
     */
    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
    }

3.  get()

    /**
     * @throws CancellationException {@inheritDoc}
     */
    public V get() throws InterruptedException, ExecutionException {
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        return report(s);
    }

 在这个get() 方法中,会判断状态是否完成,如果任务还没完成, 就等待它完成。然后执行report()方法获取任务结果。那任务结果保存在哪里? 就是在我们刚才执行run()方法的时候,已经把结果保存在Object类型的outcome变量中。所以就是要获取outcome变量的值,然后返回。

   /**
     * Returns result or throws exception for completed task.
     *
     * @param s completed state value
     */
    @SuppressWarnings("unchecked")
    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);
    }

如果任务状态是正常,就返回任务结果。 如果是取消状态,就抛出取消异常。如果是任务在执行过程中出现了异常呢?在刚才的run()方法中, 我们已经把任务执行的异常捕获保存在outcome变量中了,因此这里把 outcome变量的异常信息转为ExecutionException,然后抛出即可。 我们可以在获取线程池异步任务的结果中,捕获到异常信息,从而进行处理。

4. cancel 方法

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

如果要取消正在执行的任务,首先要把当前任务状态从NEW设置为INTERRUPTING,如果设置失败,则不能中断该任务。然后调用interrupt()方法把当前正在执行callable的线程中断, 再把任务状态改为INTERRUPTED,  最后唤醒其他线程,返回true。所以整个过程任务状态变化是:

NEW -> INTERRUPTING -> INTERRUPTED

5. 最后总结下一些注意点。  

  Future接口调用get()方法取得处理的结果值时是阻塞性的,如果调用Future对象的get()方法时,任务尚未执行完成,则调用get()方法时一直阻塞到此任务完成时为止。

Callable接口的call()方法可以有返回值,而Runnable接口的run()方法没有返回值。

Callable接口的call()方法可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常。执行完Callable接口中的任务后,返回值是通过Future接口进行获得的。

上一篇:SpringMVC接口测试异常:Can not deserialize instance of int out of START_OBJECT token


下一篇:Java基础进阶_7