FutureTask类(源码解读)

目录

一.源码解读

1.1 FutureTask(Callable callable)

1.2 FutureTask(Runnable runnable, V result)

1.3 FutureTask类的一些常量和属性

1.3.1 任务当前状态 state

1.3.2 任务7种状态属性:

1.3.3 存放返回结果 outcome:

1.3.4 指向传入的实现了Callable接口的对象索引 callable

1.3.5 运行Callable的线程 runner

1.3.6 一个带向链表结构的线程链表中的一个节点(此部分博主还不太明白)

1.4 6个public修饰的方法

1.4.1 isCancelled()

1.4.2 isDone()

1.4.3 cancel()

1.4.4 get()

1.4.5 get(long timeout, TimeUnit unit)

1.4.6 run()

1.5 5个private修饰的方法

1.5.1 finishCompletion()

1.5.2 awaitDone(boolean timed, long nanos)

1.5.3 removeWaiter(WaitNode node)

1.5.4 report(int s)

1.5.5 handlePossibleCancellationInterrupt(int s)

1.6 4个protected修饰的方法

1.6.1 set()

1.6.2 setException(Throwable t)

1.6.3 done()

1.6.4 runAndReset()

1.7 静态代码块

1.8 静态内部类


FutureTask类实现了RunnableFuture接口相关功能。RunnableFuture接口源码解读

此类的对象可以看成是一个可以对任务进行一些操作的实体,或者封装。

FutureTask类中:

2个有参构造器。

6个public修饰的方法。

5个private修饰的私有方法。

4个protected修饰的方法

1个静态内部类。

1个静态代码块。

一.源码解读

先来看看此类的两个有参的构造器

1.1 FutureTask(Callable<V> callable)

    /**
     * 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
    }

此构造器作用是传入一个实现了Callable接口的的任务类,初始化该FutureTask能操作的任务类

 

1.2 FutureTask(Runnable runnable, V result)

    /**
     * Creates a {@code FutureTask} that will, upon running, execute the
     * given {@code Runnable}, and arrange that {@code get} will return the
     * given result on successful completion.
     *
     * @param runnable the runnable task
     * @param result the result to return on successful completion. If
     * you don't need a particular result, consider using
     * constructions of the form:
     * {@code Future<?> f = new FutureTask<Void>(runnable, null)}
     * @throws NullPointerException if the runnable is null
     */
    public FutureTask(Runnable runnable, V result) {
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }

如上图此构造器的作用是传入一个实现了Runnable接口的任务类,并设置当此任务类运行完成后,任务类需要向调用此类的get方法的对象返回的 结果(result)。

此构造器会调用Executors类的静态方法 callable(Runnable task, T result) 如下图

    /**
     * Returns a {@link Callable} object that, when
     * called, runs the given task and returns the given result.  This
     * can be useful when applying methods requiring a
     * {@code Callable} to an otherwise resultless action.
     * @param task the task to run
     * @param result the result to return
     * @param <T> the type of the result
     * @return a callable object
     * @throws NullPointerException if task null
     */
    public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }

而此静态方法会返回一个实现了Executors中的静态内部类:RunnableAdapter。如下图

    /**
     * A callable that runs given task and returns given 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接口,使得实现Runnable的类也能获取返回值,虽然此返回值是初始化任务时设置的。之前只实现Runnable接口的类,线程执行完这些类后是不会有返回值的。此RunnableAdapter<T>也是对适配器模式的实现.

以上模块我们能知道,此构造器的作用就是让只实现了Runnable接口的任务类,也能拥有我们设置的result返回值。类型是个泛型。因此返回的对象很宽泛,并不是说只能返回字符串等。

由于当前FutureTask类实现了RunnableFuture接口,当然需要实现它对应的功能,看完构造器,我们来看看他public修饰的方法。来看看这些方法可以对传入的(实现了Callable或Runnable的)任务类做出那些操作。

以下对传入的任务类统称任务X(不论实现了什么接口的任务类)

1.3 FutureTask类的一些常量和属性

    /**
     * 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;

在进入第1.4之前我们需要先了解一下此类的一些属性和字段。

上图的属性都被private修饰,意义它们是此类自己使用的属性,不对外暴露。

1.3.1 任务当前状态 state

首先第一个state,顾名思义,为当前任务X的状态。它被volatile修饰,意义是同时只能被一个线程操作他的状态。

注意注释中说明了只有这四种状态的变化:

  1   * NEW -> COMPLETING -> NORMAL
  2   * NEW -> COMPLETING -> EXCEPTIONAL
  3   * NEW -> CANCELLED
  4   * NEW -> INTERRUPTING -> INTERRUPTED

1.3.2 任务7种状态属性:

NEW:代表此任务X正在从调用start()方法到任务正在运行的状态。

COMPLETING:代表此任务X运行结束,返回结果的时候,此时结果还未装载到outcome字段中(下面会提到)。完成中!

NORMAL:代表任务X执行成功,outcome字段保存正常返回值。

EXCEPTIONAL:代表任务X执行抛异常,outcome字段保存异常信息。

CANCELLED:任务X从start()方法开始到任务运行完成之前,用户调用了cancel(false)方法。因为cancel(false)会允许正在执行任务的线程执行完任务。所以任务最后会被执行完。所以从cancel(false)方法被调用到任务执行完毕。任务状态都会被置为CANCELLED。 请参考future接口中cancel的定义:Future接口(源码解读)

INTERRUPTING:任务X从start()方法开始到任务运行完成之前,用户调用了cancel(true)方法。此方法会调用interrupt()方法把中断标志置为true。当线程轮询到此标志位为true时,会做相关操作,比如真正的中断线程。例如说执行return或其他操作,抛出异常等。有关interrupt()方法可以参考博客:(先做个假链接,这里我要是忘了写,你们要么提醒我一下,要么百度一下)。

而在调用cancel(true)方法到任务被中断之前,状态标志位为INTERRUPTING

INTERRUPTED:当线程被真正中断后,任务的状态标志为此标志。

1.3.3 存放返回结果 outcome

/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes

1.3.4 指向传入的实现了Callable接口的对象索引 callable

/** The underlying callable; nulled out after running */

private Callable<V> callable;

1.3.5 运行Callable的线程 runner

/** The thread running the callable; CASed during run() */
private volatile Thread runner;

1.3.6 一个带向链表结构的线程链表中的一个节点(此部分博主还不太明白)

    /** Treiber stack of waiting threads */
    private volatile WaitNode waiters;

1.4 6个Public修饰的方法

1.4.1 isCancelled()

当任务状态大于等于CANCELLED时,返回true。

    public boolean isCancelled() {
        return state >= CANCELLED;
    }

1.4.2 isDone()

当任务状态只要不等于NEW时,返回true。由此可以说明,除了NEW的状态,都可视为任务已经运行完成了。至于完成成功与失败不做考虑。

    public boolean isDone() {
        return state != NEW;
    }

1.4.3 cancel()

实现了future接口的方法,此FutureTask类实现了RunnableFuture接口,而RunnableFuture接口又继承了future接口。future接口中有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;
    }

首先我们来看一下这个方法的第一个if判断:

当任务X当前状态不为NEW时,这里是个&&,因此后面的compareAndSwapInt方法并不会执行。

cancel方法直接返回false。

(1)意味着任务X当前的状态不为NEW时,不能使用cancel方法,cancel方法不会执行任何操作并返回false。

当任务X当前的状态为NEW时,我们再来看看这个方法。

UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)

这个方法是Unsafe类的(native)本地方法。也是对CAS思想的实现。关于CAS:

    /**
     * Atomically update Java variable to <tt>x</tt> if it is currently
     * holding <tt>expected</tt>.
     * @return <tt>true</tt> if successful
     */
    public final native boolean compareAndSwapInt(Object o, long offset,
                                                  int expected,
                                                  int x);

此方法的意思是,当存放对象o中偏移量为offset的值与期望值excepted相等时,把对象o中偏移量为offset的值修改为x

至此我们返回上面的if判断:

(2)意味着任务X当前的状态为NEW时,且传入的mayInterruptIfRunning为false时。任务的状态被切换为CANCELLED

且继续运行try部分代码,此时mayInterruptIfRunning为false,所以直接执行finally代码块中的finishCompletion()方法:

这里我们转到1.5.9节。好了看完finishCompletion()方法我们知道了只要执行cancel方法,那么waiters链表中所有节点的任务都会被取消。

(3)意味着任务X当前的状态为NEW时,且传入的mayInterruptIfRunning为true时。任务的状态被切换为INTERRUPTING并且告知线程需要被中断。执行了interrupt方法。这里我们继续来看看UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);

    /** Ordered/Lazy version of {@link #putIntVolatile(Object, long, int)}  */
    public native void    putOrderedInt(Object o, long offset, int x);

这个方法也是一个本地方法,意味着让虚拟机去设置内存屏障,避免指令重排序。作用是把offset位置的值修改为x。而且只有当offset位置的值被volatile修饰时,它被更新为x后才能立即从工作内存刷新到主内存中,这里大家应该能发现1.3.1中state就是被volatile修饰的。这里我们需要了解JMM的知识。

当然在这里就是把状态更新为INTERRUPTED

看完上面3点,我们走完了1.3.1中第3种和第4种状态变化。

1.4.4 get()

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

这方法是实现父接口的父接口future中的get方法。调用此方法的线程会阻塞到得到任务返回的结果。

里面用了awaitDone方法:请看1.6.1。

此方法意味着当任务未完成时才会去调用awaitDone的方法,看完1.6.1我们也知道,此方法会在大于COMPLETING时返回任务状态。其他状态要么yield要么park了。当大于COMPLETING状态时(可再次查看1.3.1查看状态变化)不进入awaitDone方法直接返回状态。因为只要完成之后,状态就更新了,可以直接返回状态。

1.4.5 get(long timeout, TimeUnit unit)


    /**
     * @throws CancellationException {@inheritDoc}
     */
    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);
    }

此方法和get无参方法差不多,只是当任务未结束时,已经到达了等待时间,那么会抛出超时异常。

1.4.6 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,不为NEW就直接返回了,从此可知,重复运行run方法是没有用的,同一个任务只运行一次。

如果当前的执行Callable的线程runner为null,那么他会让当前线程去成为runner。执行Callable。这也是run的意义。当然如果不为null,已经有runner去执行任务了,那么也直接run方法也直接return。

try块中的代码就是执行Callable的call方法。并获取结果result。不抛异常就调用set方法,那么我们来看一下set方法:见1.7.1节

那么当抛出异常我们需要调用setException()方法:

1.5 5个private修饰的方法

1.5.1 finishCompletion()

此方法是私有的供FutureTask类自己使用,它的作用是释放waiters单链表中所有节点的线程和任务资源。

    /**
     * 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 (;;) {//循环完所有waiters链表的节点,让线程置空,并唤醒它。
                    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
    }

在看这个方法的源码之前我们先看一下静态代码块部分,看完静态代码块部分我们看for循环,这个for循环首先定义一个WaitNode q,让他指向一个waiters节点,并且不能为空(这也是循环条件),;后不写任何自增的代码。接下来获取节点中的线程t,如果t不为空,那么让它变为空,接下来我们看看这个方法:LockSupport.unpark(t);

    /**
     * Makes available the permit for the given thread, if it
     * was not already available.  If the thread was blocked on
     * {@code park} then it will unblock.  Otherwise, its next call
     * to {@code park} is guaranteed not to block. This operation
     * is not guaranteed to have any effect at all if the given
     * thread has not been started.
     *
     * @param thread the thread to unpark, or {@code null}, in which case
     *        this operation has no effect
     */
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

这方法就是对判断thread不为空才继续调用UNSAFE的unpark方法,我们再往下跟:

    /**
     * Unblock the given thread blocked on <tt>park</tt>, or, if it is
     * not blocked, cause the subsequent call to <tt>park</tt> not to
     * block.  Note: this operation is "unsafe" solely because the
     * caller must somehow ensure that the thread has not been
     * destroyed. Nothing special is usually required to ensure this
     * when called from Java (in which there will ordinarily be a live
     * reference to the thread) but this is not nearly-automatically
     * so when calling from native code.
     * @param thread the thread to unpark.
     *
     */
    public native void unpark(Object thread);

 看上图我们看见这又是一个native方法,此方法的作用是给传入此方法的thread线程一个许可,代表它可以苏醒了。不调用此方法,如果此方法调用了park方法,他将会一直被阻塞到park操作上。

继续回到上面的finishCompletion()方法。我们看见代码中q.thread = null; LockSupport.unpark(t);首先把节点的线程置空,再把线程唤醒,相当于是把节点线程的任务结束掉,再唤醒线程,那么线程就没有任务在执行了。继续往下看能知道后面的代码就是不断都往后遍历节点,并释放资源。把每个节点的next都置空也是为了JVM在进行可达性分析算法时,能回收掉相关资源。

在这里我们不要忘了回到上面的cancel方法。我们也许是从那里跳过来的。

1.5.2 awaitDone(boolean timed, long nanos)

此方法为一个等待任务执行结束并返回任务状态的作用。可以设置最大等待时间,超过等待时间,直接返回任务状态并结束任务。

    /**
     * Awaits completion or aborts on interrupt or timeout.
     *
     * @param timed true if use timed waits
     * @param nanos time to wait, if timed
     * @return state upon completion
     */
    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);
        }
    }

看以上源码能得知此方法通过传入的布尔变量timed判断是否要设置等待超时参数。timed为true时,通过当前系统时间加上传入的nano等待时长,得到死亡线时间,for循环中,先判断当前线程是否被中断,中断则移除任务节点,并报错。这里有removeWaiter()方法:请见1.5.3

如果未被中断,那么判断当前状态是否大于COMPLETING,大于时,返回任务状态,并释放线程。意味着当线程执行完成,如下图,awaitDone方法会返回正常或异常的状态。

代码继续往下看,当前状态是COMPLETING时,那么执行Thread.yield();让线程进入就绪状态与其他线程再次竞争运行机会。下面判断queue的状态,意味for死循环中只会执行一次 queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);这段代码。循环中没有让queue再变为false的代码。除非CAS失败,意思就是线程不断轮训查看waiters节点的状态,当任务完成后,就把waiters节点置空,并返回状态。如果设置了timed状态为true,那么当死亡线时间到达,会直接移除节点任务,并返回任务状态。最后,当以上所有状态都不满足时,此线程会被park方法阻塞。

这里记得返回get方法。此方法相当于就是对get方法的实现。

1.5.3 removeWaiter(WaitNode node)

这个方法的注释是:

让超时的或中断的节点unlink掉也就是从这个链表中取出。来避免累计垃圾。

当不使用CAS时,这些节点时不能分割的,所以当释放器去遍历时,是不会对链表有影响。

为避免已删除的节点分割对未分割节点的影响,链表会重新整理。整理的过程是缓慢的,所以链表不能过长以致超过整理所计划消耗的资源。

    /**
     * Tries to unlink a timed-out or interrupted wait node to avoid
     * accumulating garbage.  Internal nodes are simply unspliced
     * without CAS since it is harmless if they are traversed anyway
     * by releasers.  To avoid effects of unsplicing from already
     * removed nodes, the list is retraversed in case of an apparent
     * race.  This is slow when there are a lot of nodes, but we don't
     * expect lists to be long enough to outweigh higher-overhead
     * schemes.
     */
    private void removeWaiter(WaitNode node) {
        if (node != null) {
            node.thread = null;
            retry:
            for (;;) {          // restart on removeWaiter race
                for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                    s = q.next;
                    if (q.thread != null)
                        pred = q;
                    else if (pred != null) {
                        pred.next = s;
                        if (pred.thread == null) // check for race
                            continue retry;
                    }
                    else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                          q, s))
                        continue retry;
                }
                break;
            }
        }
    }

1.5.4 report(int s)

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

此方法就是获取任务状态,并判断,当任务为正常完成NORMAL时返回状态,当任务s大于等于CANCELLED时,那么也许是4,5,6。当小于CANCELLED时也许是3,为什么没有0和1呢因为private修饰的report方法在每次调用的时候都已经判断大于COMPLETING  了 所以。这个report确实是对的,当状态为EXCEPTIONAL  就报执行异常ExecutionException((Throwable)x)

并抛出x异常信息,因为之前我们提到当状态为EXCEPTIONAL  时,会把异常信息装到任务状态中。可见setException方法。

    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;

1.5.5 handlePossibleCancellationInterrupt(int s)

    /**
     * Ensures that any interrupt from a possible cancel(true) is only
     * delivered to a task while in run or runAndReset.
     */
    private void handlePossibleCancellationInterrupt(int s) {
        // It is possible for our interrupter to stall before getting a
        // chance to interrupt us.  Let's spin-wait patiently.
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                Thread.yield(); // wait out pending interrupt

        // assert state == INTERRUPTED;

        // We want to clear any interrupt we may have received from
        // cancel(true).  However, it is permissible to use interrupts
        // as an independent mechanism for a task to communicate with
        // its caller, and there is no way to clear only the
        // cancellation interrupt.
        //
        // Thread.interrupted();
    }

1.6 4个protected修饰的方法

1.6.1 set()

    /**
     * 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();
        }
    }

看完上面的内容,相信大家对这个UNSAFE里的方法很熟悉了。那么set方法就是把NEW状态更新为COMPLETING,更新成功就把v结果值赋值给outcome,赋值完成后,再把状态更新为NORMAL。最后释放各种资源调用finishCompletion方法。见1.5.9节。

看完set方法,我们走完了1.3.1中第1种状态变化。

1.6.2 setException(Throwable t)

    /**
     * Causes this future to report an {@link ExecutionException}
     * with the given throwable as its cause, unless this future has
     * already been set or has been cancelled.
     *
     * <p>This method is invoked internally by the {@link #run} method
     * upon failure of the computation.
     *
     * @param t the cause of failure
     */
    protected void setException(Throwable t) {
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
            finishCompletion();
        }
    }

如果我们看完了set方法再来看此方法,那么能知道它就是走的1.3.1节中的第二种状态。

相信看完这里我们已经走完了1.3.1节任务状态中的第4种状态。

1.6.3 done()

此方法是一个回调函数,虽然在本类中此方法没有任何实现,但是我们仔细观察会发现,此done方法在本类中被finishCompletion()方法释放所有资源后最后调用了(请见1.5.1)。然而finishCompletion()又被set方法(请见1.6.1)和setException(Throwable t)和cancel方法(请见1.4.3)调用。这3个方法我们看完就能知道他设置改变了1.3.1中说明会发生的4种状态变化。从而知道done方法会在每个流程走完的时候被调用,每种流程最后都调用了finishCompletion(),而finishCompletion()最后又会调用done。所以当子类实现此方法后,可以在这方法中写一些任务执行完需要执行的代码。finishCompletion()会去回调它。

    /**
     * Protected method invoked when this task transitions to state
     * {@code isDone} (whether normally or via cancellation). The
     * default implementation does nothing.  Subclasses may override
     * this method to invoke completion callbacks or perform
     * bookkeeping. Note that you can query status inside the
     * implementation of this method to determine whether this task
     * has been cancelled.
     */
    protected void done() { }

栗子:task是传入实现了Runnable或Callable的任务(类)。

        FutureTask<Integer> future = new FutureTask<Integer>(task) {
            @Override
            protected void done() {
                try {
                    System.out.println("任务执行完毕,执行结果为:" + get());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }
        };

1.6.4 runAndReset()

此方法从名字上很好懂,运行并重置。此方法在本类中没有被任何其他方法调用

先来看看它的注释:

在不设置结果的情况下执行计算,然后将此任务重置为初始状态,如果计算遇到异常或被取消,则无法执行此操作。这是为原本会执行多次的任务的需求而设计的。

意思就是当一个任务执行之后会改变一些运行前的环境,而不能重复运行任务,而这个方法就是保证每次run正常执行后,都能重置并再次运行。

    /**
     * Executes the computation without setting its result, and then
     * resets this future to initial state, failing to do so if the
     * computation encounters an exception or is cancelled.  This is
     * designed for use with tasks that intrinsically execute more
     * than once.
     *
     * @return {@code true} if successfully run and reset
     */
    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;
    }

 一下代码我们在run方法中解释过了,是保证当前只有一个线程在运行任务,不论换哪一个线程来执行任务,任务都只有一个任务。如果当前没有线程执行本任务,那么把任务类中记录的运行本任务线程的变量置为本线程(此变量被存放在本类偏移量为runnerOffset的位置)。就是说没有线程运行本任务那么就由我来。

        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return false;

此方法对比run方法,他关键的地方就是没有调用set方法。而set方法做了啥呢(参见1.6.1节)?他相当于让任务状态进行了 4种状态的第一种:

NEW -> COMPLETING -> NORMAL

如果不调用set方法的话,任务状态始终是NEW。所以我们能知道。runAndReset()的核心功能是,在运行完成后并不改变此任务的状态。以此达到多次运行的效果,比如当前有个需求是运行此任务4次算完成任务。那么前3次我可以调用runAndReset(),最后以此调用run(),从run方法或runAndReset中我们是能知道只有当状态为NEW时,任务才会运行的。其他状态时都会直接return了。

补充一下,为什么其他状态我们不考虑呢?因为其他状态要么是中断要么就是取消了,要么是异常了。只有正常运行完成我们才会继续任务对吧。因此才会在返回时有判断 return ran && s == NEW;代表ran要是true运行成功,并且没有被取消和中断。s需要判断为NEW才算reset。

1.7 静态代码块

    // 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"));//获取当前k类中任务的状态属性偏移量
            runnerOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("runner"));//获取当前执行Callable方法的线程的偏移量
            waitersOffset = UNSAFE.objectFieldOffset
                (k.getDeclaredField("waiters"));//获取k类的waiters等待线程节点偏移量
        } catch (Exception e) {
            throw new Error(e);
        }
    }

上面代码中可以看出,先获取到了FutureTask类的Class对象k。然后获取它的state属性,runner属性,waiters属性,的偏移量。这三个属性都能在类中源码看见。

1.8 静态内部类

本类中执行任务的线程等待链表的一个节点的构造类。

    /**
     * Simple linked list nodes to record waiting threads in a Treiber
     * stack.  See other classes such as Phaser and SynchronousQueue
     * for more detailed explanation.
     */
    static final class WaitNode {
        volatile Thread thread;
        volatile WaitNode next;
        WaitNode() { thread = Thread.currentThread(); }
    }

 

二.类的使用实践

待完善...

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章