Java ~ Executor ~ FutureTask【源码】

前言


 文章

一 FutureTask(未来任务)类源码及机制详解


 类

    FutureTask(未来任务)类是RunnableFuture(可运行任务)接口的实现类,故而其实例为Future(未来)的同时也是Runnable(可运行)。这意味着其不仅可以代表任务,本身也可以作为任务执行。这使得未来任务类具备了静态代理的特性,即支持在内部代理任务执行的上下文环境中自定义代理行为以实现设计需求,该知识点会在下文详述。

    未来任务类是线程安全的。未来任务类采用“无锁”线程安全机制,即使用CAS乐观锁来保证整体的线程安全。由于CAS乐观锁并不是真正意义上的锁,因此被称为“无锁”线程安全机制。

/**
 * A cancellable asynchronous computation.  This class provides a base implementation of {@link Future}, with methods to start and cancel
 * a computation, query to see if the computation is complete, and retrieve the result of the computation.  The result can only be retrieved
 * when the computation has completed; the {@code get} methods will block if the computation has not yet completed.  Once the computation
 * has completed, the computation cannot be restarted or cancelled (unless the computation is invoked using {@link #runAndReset}).
 * 一个可取消的异步计算,这个类提供一个未来的基础实现,包含开始及取消一个计算,查询并检查计算是否完成,以及检索计算的结果的方法。
 * 该结果只能在计算完成时检索;如果计算还没有完成则get()方法将阻塞。计算一旦完成,这个计算无法再重新开始或取消(除非这个计算调用
 * 了runAndReset()方法)。
 * <p>
 * A {@code FutureTask} can be used to wrap a {@link Callable} or {@link Runnable} object.  Because {@code FutureTask} implements
 * {@code Runnable}, a {@code FutureTask} can be submitted to a {@link Executor} for execution.
 * 未来任务类能够包装可调用及可运行对象。因为未来任务类实现于可运行接口,一个未来任务可以可以递交给执行器执行。
 * <p>
 * In addition to serving as a standalone class, this class provides {@code protected} functionality that may be useful when creating
 * customized task classes.
 * 除了作为一个独立的类,该类还提供"保护"功能,这在创建定制任务类时可能会很有用。
 *
 * @param <V> The result type returned by this FutureTask's {@code get} methods 未来任务的get()方法返回的结果类型
 * @author Doug Lea
 * @Description: 未来任务类
 * @since 1.5
 */
public class FutureTask<V> implements RunnableFuture<V> {
    /*
     * Revision notes: This differs from previous versions of this class that relied on AbstractQueuedSynchronizer, mainly to avoid surprising
     * users about retaining interrupt status during cancellation races. Sync control in the current design relies on a "state" field updated via
     * CAS to track completion, along with a simple Treiber stack to hold waiting threads.
     * 修改简介:类的当前版本与依赖于AQS的早期版本不同,主要是为了避免在取消竞争期间保留中断状态的问题另用户感到惊讶。同步控制在
     * 当前设计中依赖于一个通过CAS进行更新的"state"字段来追踪完成状态,以及一个简单的Treiber堆栈来持有等待线程。
     * Style note: As usual, we bypass overhead of using AtomicXFieldUpdaters and instead directly use Unsafe intrinsics.
     * 风格简介:像往常一样,我们绕过了使用AtomicXFieldUpdaters的开销,并且直接使用固有的Unsafe。
     */
}

 字段

  • state —— 状态 —— 表示当前任务的执行状态。
    /**
     * 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.
     * 任务的运行状态,初始为NEW(0:新)。运行状态只能在set()、setException()和cancel()方法中转变为终止状态。在完成期间,状态可能呈
     * 现短暂的COMPLETING(1:执行中)(当结果正在被设定时)或INTERRUPTING(5:中断中)(只在中断运行程序满足cancel(true)时)。状
     * 态从中间值转变为最终值使用廉价的命令/延迟写入,因为「状态」值是唯一且无法进一步修改的。
     * <p>
     * Possible state transitions:
     * 可能的状态转变:
     * NEW -> COMPLETING -> NORMAL
     * (0:新) -> (1:执行中) -> (2:标准)
     * NEW -> COMPLETING -> EXCEPTIONAL
     * (0:新) -> (1:执行中) -> (3:异常)
     * NEW -> CANCELLED
     * (0:新) -> (4:已取消)
     * NEW -> INTERRUPTING -> INTERRUPTED
     * (0:新) -> (5:中断中) -> (6:已中断)
     *
     * @Description: 名称:状态
     * @Description: 作用:表示当前任务的执行状态。
     * @Description: 逻辑:~
     */
    private volatile int state;
  • NEW —— 新 —— 表示当前任务实际正在等待/执行中。
    /**
     * @Description: 名称:新
     * @Description: 作用:表示当前任务实际正在等待/执行中。
     * @Description: 逻辑:~
     */
    private static final int NEW = 0;
  • COMPLETING —— 执行中 —— 表示当前任务实际已完成/异常,但还没有在未来任务中保存结果/异常。
    /**
     * @Description: 名称:执行中
     * @Description: 作用:表示当前任务实际已完成/异常,但还没有在未来任务中保存结果/异常。
     * @Description: 逻辑:~
     */
    private static final int COMPLETING = 1;
  • NORMAL —— 正常 —— 表示当前任务实际已完成,并已在未来任务中保存结果。
    /**
     * @Description: 名称:正常
     * @Description: 作用:表示当前任务实际已完成,并已在未来任务中保存结果。
     * @Description: 逻辑:~
     */
    private static final int NORMAL = 2;
  • EXCEPTIONAL —— 异常 —— 表示当前任务实际已异常,并已在未来任务中保存异常。
    /**
     * @Description: 名称:异常
     * @Description: 作用:表示当前任务实际已异常,并已在未来任务中保存异常。
     * @Description: 逻辑:~
     */
    private static final int EXCEPTIONAL = 3;
  • CANCELLED —— 已取消 —— 表示当前任务实际已取消。当mayInterruptIfRunning(如果运行可能中断)参数为false时会修改为该状态。如果此时当前任务实际正在等待中则阻止其执行;而如果当前任务实际正在执行中则会继续执行,但会阻止其设置结果/异常。
    /**
     * @Description: 名称:已取消
     * @Description: 作用:表示当前任务实际已取消。当mayInterruptIfRunning(如果运行可能中断)参数为false时会修改为该状态。如果此时
     * @Description: 当前任务实际正在等待中则阻止其执行;而如果当前任务实际正在执行中则会继续执行,但会阻止其设置结果/异常。
     * @Description: 逻辑:~
     */
    private static final int CANCELLED = 4;
  • INTERRUPTING —— 中断中 —— 表示当前任务正在中断。当mayInterruptIfRunning(如果运行可能中断)参数为true时会修改为该状态,并尝试对执行线程施以中断操作以尝试取消任务。但由于Java只有响应式中断,故而任务能否取消并无法确定,因此实际上中断操作执行后并不会关注结果,而是阻止结果的赋值。即处于中断中状态的任务不允许保存结果,置于能否中断则听天由命。
    /**
     * @Description: 名称:中断中
     * @Description: 作用:表示当前任务正在中断。当mayInterruptIfRunning(如果运行可能中断)参数为true时会修改为该状态,并尝试对执
     * @Description: 行线程施以中断操作以尝试取消任务。但由于Java只有响应式中断,故而任务能否取消并无法确定,因此实际上中断操作执
     * @Description: 行后并不会关注结果,而是阻止结果的赋值。即处于中断中状态的任务不允许保存结果,置于能否中断则听天由命。
     * @Description: 逻辑:~
     */
    private static final int INTERRUPTING = 5;
  • INTERRUPTED —— 已中断 —— 表示当前任务已执行取消操作,即不可能会有结果。
    /**
     * @Description: 名称:已中断
     * @Description: 作用:表示当前任务已执行取消操作,即不可能会有结果。
     * @Description: 逻辑:~
     */
    // 已中断:表示已经执行了中断操作,中断的具体结果就不管了,反正也付不了值。
    private static final int INTERRUPTED = 6;
  • callable —— 可调用 —— 持有任务的引用。
    /**
     * The underlying callable; nulled out after running
     * 底层的可调用对象;运行后为null
     *
     * @Description: 名称:可调用
     * @Description: 作用:持有任务的引用。
     * @Description: 逻辑:~
     */
    private Callable<V> callable;
  • outcome —— 结果 —— 保存任务执行的正常/异常结果。
    /**
     * The result to return or exception to throw from get()
     * 来自于get()方法返回的结果或抛出的异常
     *
     * @Description: 名称:结果
     * @Description: 作用:保存任务执行的正常/异常结果。
     * @Description: 逻辑:~
     */
    // non-volatile, protected by state reads/writes
    // 非volatile,受保护于状态读取/写入
    private Object outcome;
  • runner —— 行者 —— 运行任务的线程,通过CAS操作设置。同时可与NEW(0:新)状态共同表示当前任务实际是等待中还是执行中。
    /**
     * The thread running the callable; CASed during run()
     * 运行任务的线程,在运行期间通过CAS设置。
     *
     * @Description: 名称:运行者
     * @Description: 作用:运行任务的线程,通过CAS操作设置。同时可与NEW(0:新)状态共同表示当前任务实际是等待中还是执行中。
     * @Description: 逻辑:~
     */
    private volatile Thread runner;
  • waiters —— 等待者链表 —— 保存等待任务执行结果的所有线程。所谓的等待着即调用当前未来get()方法的线程,这些线程会封装为节点以链表的方式保存。该链表是逻辑链表,即通过持有链表的头节点来变相持有整个链表。
    /**
     * Treiber stack of waiting threads
     * 等待线程的Treiber堆栈
     *
     * @Description: 名称:等待者链表
     * @Description: 作用:保存等待任务执行结果的所有线程。所谓的等待着即调用当前未来get()方法的线程,这些线程会封装为节点以链
     * @Description: 表的方式保存。该链表是逻辑链表,即通过持有链表的头节点来变相持有整个链表。
     * @Description: 逻辑:~
     */
    private volatile WaitNode waiters;

 构造方法

  • public 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.
     * 创建一个未来任务,它将在运行时执行给定的可运行任务,并安排get()方法在成功完成时返回指定的结果。
     *
     * @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)}
     *                 在成功完成时返回结果。如果你不需要一个特定的结果,考虑使用以下结构:
     *                 {@code Future<?> f = new FutureTask<Void>(runnable, null)}。
     * @throws NullPointerException if the runnable is null
     *                              空指针异常:如果可运行任务为null
     * @Description: 名称:~
     * @Description: 作用:创建一个指定可运行任务的未来任务,并同步传入一个用于承载可运行任务执行结果的变量。
     * @Description: 逻辑:方法会将传入的可运行任务及用于承载可运行任务执行结果的变量封装为可调用任务。
     */
    public FutureTask(Runnable runnable, V result) {
        // 将Runnable封装为Callable,这一步是通过Executors的内部适配器类RunnableAdapter实现的,使用了适配器模式。
        this.callable = Executors.callable(runnable, result);
        // 设置初始状态值。
        this.state = NEW;       // ensure visibility of callable
    }

 静态块

    CAS机制。

    // 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对象并计算各个字段的偏移量用于CAS操作。
            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);
        }
    }

 方法

  • private V report(int s) throws ExecutionException —— 报告 —— 当当前任务实际执行结束时,根据当前状态执行返回任务结果/抛出取消异常/抛出执行异常异常三类操作。
        方法会根据传入的状态进行判断,如果状态为NORMAL(2:正常),说明当前任务已完成,直接返回结果;如果状态为CANCELLED(4:已取消)/INTERRUPTING(5:中断中)/INTERRUPTED(6:已中断),则表示当前任务已取消;直接抛出取消异常;如果状态为EXCEPTIONAL(3:异常),说明任务本身出现了问题,抛出任务执行异常。
    /**
     * Returns result or throws exception for completed task.
     * 任务完成时返回结果或抛出异常
     *
     * @param s completed state value 完成状态值
     * @Description: 名称:报告
     * @Description: --------------------------------------------------------
     * @Description: 作用:当当前任务实际执行结束时,根据当前状态执行返回任务结果/抛出取消异常/抛出执行异常异常三类操作。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法会根据传入的状态进行判断,如果状态为NORMAL(2:正常),说明当前任务已完成,直接返回结果;如果
     * @Description: 状态为CANCELLED(4:已取消)/INTERRUPTING(5:中断中)/INTERRUPTED(6:已中断),则表示当前任务已取
     * @Description: 消;直接抛出取消异常;如果状态为EXCEPTIONAL(3:异常),说明任务本身出现了问题,抛出任务执行异常。
     */
    @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);
    }
  • public boolean isCancelled() —— 是否取消 —— 判断当前未来任务所关联的任务是否取消,是则返回true;否则返回false。
        方法会判断任务的状态是否为CANCELLED(4:已取消)/INTERRUPTING(5:中断中)/INTERRUPTED(6:已中断),这些状态都表示任务已被取消,是则返回true;否则返回false。
    /**
     * @Description: 名称:是否取消
     * @Description: --------------------------------------------------------
     * @Description: 作用:判断当前未来任务所关联的任务是否取消,是则返回true;否则返回false。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法会判断任务的状态是否为CANCELLED(4:已取消)/INTERRUPTING(5:中断中)/INTERRUPTED(6:已
     * @Description: 中断),这些状态都表示任务已被取消,是则返回true;否则返回false。
     */
    @Override
    public boolean isCancelled() {
        return state >= CANCELLED;
    }
  • public boolean isDone() —— 是否结束 —— 判断当前未来任务所关联的任务是否结束(完成/异常/取消),是则返回true;否则返回false。
        方法会判断任务的状态是否不为NEW(0:新),因为NEW(0:新)表示实际的等待中/执行中。因此除了该状态以外其它所有的状态都表示任务已结束(完成/异常/取消)…当然,按未来任务的定义,可能还没有保存任务结果。
    /**
     * @Description: 名称:是否结束
     * @Description: --------------------------------------------------------
     * @Description: 作用:判断当前未来任务所关联的任务是否结束(完成/异常/取消),是则返回true;否则返回false。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法会判断任务的状态是否不为NEW(0:新),因为NEW(0:新)表示实际的等待中/执行中。因此除了该状
     * @Description: 态以外其它所有的状态都表示任务已结束(完成/异常/取消)...当然,按未来任务的定义,可能还没有保存任务结果。
     */
    @Override
    public boolean isDone() {
        return state != NEW;
    }
  • public boolean cancel(boolean mayInterruptIfRunning) —— 取消 —— 取消当前未来任务所关联的任务。如果任务已结束(完成/异常/取消),由于已经处于最终状态,因此对于该类任务将无法被取消;而对于非结束任务,如果mayInterruptIfRunning(如果运行可能中断)参数为false,则方法只会阻止等待中的任务执行;如果为true,则方法还会尝试取消正在执行中的任务,虽然任务可能无法被取消。如果任务被成功取消则返回true;否则返回false。
        方法会判断任务状态是否为NEW(0:新),如果不是,意味着当前任务执行执行结束了,无法再被取消,直接返回false;如果是,则通过CAS操作修改状态。使用CAS操作的原因是为了避免并发影响,例如存在多个线程同时取消任务或任务因为执行而状态发生改变的情况,状态值的具体修改与mayInterruptIfRunning(如果运行可能中断)参数有关,如果其为false,则将状态修改为CANCELLED(4:已取消);否则修改为INTERRUPTING(5:已中断)。如果修改失败,意味着有其它线程修改了任务的状态(可能是其它外来线程,也可能是执行任务的线程),直接返回false。
        状态修改成功后,如果mayInterruptIfRunning(如果运行可能中断)参数为true,则还需要尝试取消正在执行的任务。任务是否执行中通过是否存在运行者判断。由于中断可能会导致任务抛出异常,因此需要try块中进行。try块的作用并不是为了捕获异常,而是防止异常的抛出破坏了任务执行的整体流程。成功对实际执行中的任务施加中断后,还需要将状态修改为INTERRUPTED(6:已中断),该修改不会使用CAS操作,而是使用一个顺序/延迟指令完成,因为该状态只是为了流程的规范,并不会对判断造成影响。
        最后,当上述所有的流程结束,还需要唤醒所有的等待者以令之抛出取消异常。
    /**
     * @Description: 名称:取消
     * @Description: --------------------------------------------------------
     * @Description: 作用:取消当前未来任务所关联的任务。如果任务已结束(完成/异常/取消),由于已经处于最终状态,因此对于该类
     * @Description: 任务将无法被取消;而对于非结束任务,如果mayInterruptIfRunning(如果运行可能中断)参数为false,则方法只会阻
     * @Description: 止等待中的任务执行;如果为true,则方法还会尝试取消正在执行中的任务,虽然任务可能无法被取消。如果任务被成
     * @Description: 功取消则返回true;否则返回false。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法会判断任务状态是否为NEW(0:新),如果不是,意味着当前任务执行执行结束了,无法再被取消,直接
     * @Description: 返回false;如果是,则通过CAS操作修改状态。使用CAS操作的原因是为了避免并发影响,例如存在多个线程同时取消
     * @Description: 任务或任务因为执行而状态发生改变的情况,状态值的具体修改与mayInterruptIfRunning(如果运行可能中断)参数有
     * @Description: 关,如果其为false,则将状态修改为CANCELLED(4:已取消);否则修改为INTERRUPTING(5:已中断)。如果修改
     * @Description: 失败,意味着有其它线程修改了任务的状态(可能是其它外来线程,也可能是执行任务的线程),直接返回false。
     * @Description: 状态修改成功后,如果mayInterruptIfRunning(如果运行可能中断)参数为true,则还需要尝试取消正在执行的任务。任
     * @Description: 务是否执行中通过是否存在运行者判断。由于中断可能会导致任务抛出异常,因此需要try块中进行。try块的作用并不
     * @Description: 是为了捕获异常,而是防止异常的抛出破坏了任务执行的整体流程。成功对实际执行中的任务施加中断后,还需要将状
     * @Description: 态修改为INTERRUPTED(6:已中断),该修改不会使用CAS操作,而是使用一个顺序/延迟指令完成,因为该状态只是
     * @Description: 为了流程的规范,并不会对判断造成影响。
     * @Description: 最后,当上述所有的流程结束,还需要唤醒所有的等待者以令之抛出取消异常。
     */
    @Override
    public boolean cancel(boolean mayInterruptIfRunning) {
        // 如果状态为NEW(0:新),说明任务实际正在等待中/执行中,是可以被取消的,因此通过CAS修改状态值。之所以使用CAS操作是因为
        // 可能存在多个线程都试图取消该任务造成竞争。状态设置依赖于mayInterruptIfRunning(如果运行可能中断)参数,如果
        // mayInterruptIfRunning(如果运行可能中断)参数为false,则将任务状态修改为CANCELLED(4:已取消);否则修改为INTERRUPTING(
        // 5:中断中)。如果CAS操作失败了,意味着有其它线程成功取消了任务,因此直接返回false。
        if (!(state == NEW && UNSAFE.compareAndSwapInt(this, stateOffset, NEW, mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        // mayInterruptIfRunning(如果运行可能中断)参数为true,则需要中断执行任务的线程。由于中断可能会导致任务抛出异常,因此需要在
        // try块中进行。try块的作用并不是为了捕获异常,而是防止异常的抛出破坏了任务执行的整体流程。
        try {
            // in case call to interrupt throws exception
            // 万一调用中断抛出异常...这能有啥异常?
            if (mayInterruptIfRunning) {
                try {
                    // 判断运行者是否存在,如果存在,则说明当前任务正在执行中,对之进行中断。
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally {
                    // final state
                    // 最终状态

                    // 任务中断操作执行后,将任务修改为INTERRUPTED(6:已中断)最终状态。最终状态的修改使用一个顺序/延迟的指令执行,
                    // 因为该状态只为了展示,并不会影响运行流程。
                    UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
                }
            }
        } finally {
            // 当任务被取消后,需要结束当前的计算,主要是通知等待者。
            finishCompletion();
        }
        return true;
    }

  • public V get() throws InterruptedException, ExecutionException —— 获取 —— 获取当前任务的执行结果,如果任务正在执行则会等待,直至任务结束(完成/异常/取消)而获取到结果或抛出取消/执行异常。
        方法首先会判断当前任务的状态,如果任务实际已结束(即状态 > 0),则可以直接从outcome(结果)字段中获取结果或执行异常,由或者直接根据state(状态)字段来抛出取消异常。但如果任务实际还在等待/执行中,则需要令当前线程等待,直至任务结束(完成/异常/取消)而被唤醒,该逻辑由awaitDone(boolean timed, long nanos)方法完成。
    /**
     * @throws CancellationException {@inheritDoc} 取消异常
     * @Description: 名称:获取
     * @Description: --------------------------------------------------------
     * @Description: 作用:获取当前任务的执行结果,如果任务正在执行则会等待,直至任务结束(完成/异常/取消)而获取到结果或抛出
     * @Description: 取消/执行异常。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法首先会判断当前任务的状态,如果任务实际已结束(即状态 > 0),则可以直接从outcome(结果)字段中获
     * @Description: 取结果或执行异常,由或者直接根据state(状态)字段来抛出取消异常。但如果任务实际还在等待/执行中,则需要令
     * @Description: 当前线程等待,直至任务结束(完成/异常/取消)而被唤醒,该逻辑由awaitDone(boolean timed, long nanos)方法完成。
     */
    @Override
    public V get() throws InterruptedException, ExecutionException {
        // 获取状态(快照),如果状态(快照)<= COMPLETING(1:执行中),则说明任务实际处于未执行或执行中的状态,需要阻塞。
        int s = state;
        if (s <= COMPLETING)
            s = awaitDone(false, 0L);
        // 返回运行结果或异常。
        return report(s);
    }
  • public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException —— 获取 —— 获取当前任务的执行结果,如果任务正在执行则会等待指定等待时间,如果在指定等待时间之前任务执行结束(完成/异常/取消)则获取结果或抛出取消/执行异常;否则抛出超时异常。
        方法首先会判断当前任务的状态,如果任务实际已结束(即状态 > 0),则可以直接从outcome(结果)字段中获取结果或执行异常,由或者直接根据state(状态)字段来抛出取消异常。但如果任务实际还在等待/执行中,则需要令当前线程等待指定等待时间,直至任务结束(完成/异常/取消)而被唤醒或抛出超时异常,该逻辑由awaitDone(boolean timed, long nanos)方法完成。
    /**
     * @throws CancellationException {@inheritDoc}
     * @Description: 名称:获取
     * @Description: --------------------------------------------------------
     * @Description: 作用:获取当前任务的执行结果,如果任务正在执行则会等待指定等待时间,如果在指定等待时间之前任务执行结束(
     * @Description: 完成/异常/取消)则获取结果或抛出取消/执行异常;否则抛出超时异常。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法首先会判断当前任务的状态,如果任务实际已结束(即状态 > 0),则可以直接从outcome(结果)字段中获
     * @Description: 取结果或执行异常,由或者直接根据state(状态)字段来抛出取消异常。但如果任务实际还在等待/执行中,则需要令
     * @Description: 当前线程等待指定等待时间,直至任务结束(完成/异常/取消)而被唤醒或抛出超时异常,该逻辑由
     * @Description: awaitDone(boolean timed, long nanos)方法完成。
     */
    @Override
    public V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
        if (unit == null)
            throw new NullPointerException();
        // 获取状态(快照),如果状态(快照)<= COMPLETING(1:执行中),则说明任务处于未执行或执行中的状态,需要阻塞。
        int s = state;
        //     如果在执行限时阻塞后返回的状态值为NEW(0:新)或COMPLETING(1:执行中),说明任务在指定的时间内没有执行或没
        // 有执行完毕,因此直接抛出超时异常。
        if (s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)
            throw new TimeoutException();
        // 返回运行结果或异常。
        return report(s);
    }
  • protected void done() —— 结束 —— 用于子类实现在任务结束(完成/异常/取消)并唤醒等待线程后可以执行一些自定义操作。
    /**
     * 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.
     * 当这个任务转变状态为结束(即非等待中状态)(不论是正常或通过取消结束)时调用的受保护方法。默认实现什么也不做。子类可
     * 能为了调用回调或执行统计而重写这个方法(执行一些自定义操作)。注意:你可以在这个方法的实现中查询状态以查明这个任务是
     * 否已经取消(该方法的作用就是在取消操作(状态修改 + 等待者换新)后执行一些自定义的操作)。
     *
     * @Description: 名称:结束
     * @Description: --------------------------------------------------------
     * @Description: 作用:用于子类实现在任务结束(完成/异常/取消)并唤醒等待线程后可以执行一些自定义操作。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:~
     */
    protected void done() {
    }
  • protected void set(V v) —— 设置 —— 将当前的任务执行结果存入当前未来任务中。
        方法会首先会将当前任务的状态有NEW(0:新)通过CAS操作修改为COMPLETING(1:执行中),表示任务已完成,但还没有保存结果。随后在保存结果后再将状态修改为NORMAL(2:正常)。由于是最终状态,并且不影响整体流程判断,因此会使用一个顺序/延迟的命令来赋予该最终状态值。最后会唤醒所有等待者来获取结果。
    /**
     * Sets the result of this future to the given value unless this future has already been set or has been cancelled.
     * 设置future的结果为指定的值,除非future已经设置或已经取消。
     * <p>
     * This method is invoked internally by the {@link #run} method upon successful completion of the computation.
     * 这个方法在成功完成计算后(即在执行过程中没有抛出异常)通过run()方法内部地调用。
     *
     * @param v the value 值
     * @Description: 名称:设置
     * @Description: --------------------------------------------------------
     * @Description: 作用:将当前的任务执行结果存入当前未来任务中。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法会首先会将当前任务的状态有NEW(0:新)通过CAS操作修改为COMPLETING(1:执行中),表示任务已
     * @Description: 完成,但还没有保存结果。随后在保存结果后再将状态修改为NORMAL(2:正常)。由于是最终状态,并且不影响整
     * @Description: 体流程判断,因此会使用一个顺序/延迟的命令来赋予该最终状态值。最后会唤醒所有等待者来获取结果。
     */
    protected void set(V v) {
        //     通过CAS操作将任务的状态修改为COMPLETING(1:执行中)...所以实际上在COMPLETING(1:执行中)状态下任务已经完成
        // 了执行,只是还没有对Future设置完成结果。
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            // 设置结果并使用延迟的命令设置任务状态为NORMAL(2:正常)。
            outcome = v;
            // final state
            // 最终状态
            UNSAFE.putOrderedInt(this, stateOffset, NORMAL);
            // 结束计算,即唤醒所有的等待者。
            finishCompletion();
        }
    }
  • protected void setException(Throwable t) —— 设置异常 —— 将当前的任务执行异常存入当前未来任务中。
        方法会首先会将当前任务的状态有NEW(0:新)通过CAS操作修改为COMPLETING(1:执行中),表示任务已异常,但还没有保存异常。随后在保存异常后再将状态修改为EXCEPTIONAL(3:异常)。由于是最终状态,并且不影响整体流程判断,因此会使用一个顺序/延迟的命令来赋予该最终状态值。最后会唤醒所有等待者来抛出执行异常。
    /**
     * 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.
     * 导致此future报告一个ExecutionException,并将给定的可抛出对象作为其原因,除非future已经设置或已经取消。
     * <p>
     * This method is invoked internally by the {@link #run} method upon failure of the computation.
     * 这个方法在计算失败后(即在执行过程中没有抛出异常)通过run()方法内部地调用。
     *
     * @param t the cause of failure 失败的原因
     * @Description: 名称:设置异常
     * @Description: --------------------------------------------------------
     * @Description: 作用:将当前的任务执行异常存入当前未来任务中。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法会首先会将当前任务的状态有NEW(0:新)通过CAS操作修改为COMPLETING(1:执行中),表示任务已
     * @Description: 异常,但还没有保存异常。随后在保存异常后再将状态修改为EXCEPTIONAL(3:异常)。由于是最终状态,并且不影
     * @Description: 响整体流程判断,因此会使用一个顺序/延迟的命令来赋予该最终状态值。最后会唤醒所有等待者来抛出执行异常。
     */
    protected void setException(Throwable t) {
        // 通过CAS操作修改状态,并设置异常结果,并设置最终结果即唤醒所有的等待者。
        if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
            outcome = t;
            // final state
            // 最终结果
            UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL);
            finishCompletion();
        }
    }
  • public void run() —— 运行 —— 执行封装在未来任务中的任务。
    /**
     * @Description: 名称:运行
     * @Description: --------------------------------------------------------
     * @Description: 作用:执行封装在未来任务中的任务。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:~
     */
    @Override
    public void run() {
        //     任务只有在状态NEW(0:新)和runner(运行者)字段为null的情况下才可以执行,否则便意味着任务实际上正在执行或已经结
        // 束(完成/异常/取消)。在满足上述条件的情况意味着任务实际处于等待中的状态,而当通过CAS操作将当前线程成功设置为运行
        // 者后,意味着当前任务实际进入了执行中的状态。如果不满足条件或CAS运行者执行失败,则方法会直接返回。
        if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
            return;
        try {
            //     确保任务存在且状态为NEW(0:新),在上述的判断中存在空隙,因此即使成功通过CAS操作设置了运行者,任务也可能已
            // 经被取消了,因此在正式执行之前,是需要进行一次判断的。
            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);
                }
                // 如果执行成功,则设置正常结果...感觉这个标记位为什么必要,直接在try块等待尾部赋值不就好了?
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to prevent concurrent calls to run()
            // 运行者必须不为空直到状态被设置,以防止并发调用run()。

            // 任务执行完成后需要把runner(运行者)字段设置为null。
            runner = null;
            // state must be re-read after nulling runner to prevent leaked interrupts
            // runner(运行者)字段为null后状态必须重新读取以防止中断泄露...中断泄露?
            int s = state;
            // 如果状态为中断相关,则令线程放弃CPU资源以减缓执行。
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
  • protected boolean runAndReset() —— 运行并重置 —— 执行封装在未来任务中的任务但不保存执行结果,并返回是否可再次执行。如果可以则返回true;否则返回false。
        方法与run()方法很相似,两者的区别在于runAndReset()方法并不会保存任务的正常结果,因此只要任务未被取消或执行异常,则可以多次执行。
    /**
     * 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.
     * 在不设置计算结果的情况下执行计算,然后将此future重置为初始状态,如果计算遇到异常或被取消,则无法执行此操作。这是为使用
     * 内在执行多次的任务而设计的。
     *
     * @return {@code true} if successfully run and reset 如果成功执行并充值
     * @Description: 名称:运行并重置
     * @Description: --------------------------------------------------------
     * @Description: 作用:执行封装在未来任务中的任务但不保存执行结果,并返回是否可再次执行。如果可以则返回true;否则返回false。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法与run()方法很相似,两者的区别在于runAndReset()方法并不会保存任务的正常结果,因此只要任务未被取消
     * @Description: 或执行异常,则可以多次执行。
     */
    protected boolean runAndReset() {
        //     任务会有在状态为NEW(0:新)且runner(运行者)字段为null的情况下才可以执行,返回便直接返回。任务只有在NEW(0:新)
        // 状态下才表示可执行,但实际情况是NEW(0:新)还可能已经是实际执行的状态,因此还需要根据runner(运行者)字段进行判断,
        // 如果runner(运行者)字段不为null则说明有现成正在执行。
        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()
            // 运行者必须不为空直到状态被设置,以防止并发调用run()。

            // 任务执行完成后需要把runner(运行者)字段设置为null。
            runner = null;
            // state must be re-read after nulling runner to prevent leaked interrupts
            // runner(运行者)字段为null后状态必须重新读取以防止中断泄露...中断泄露?
            s = state;
            // 如果状态为中断相关,则令线程放弃CPU资源以减缓执行。
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
        //     返回是否执行成功且状态是否为NEW(0:新)。如果状态被修改,则打破了"重置"的思想。所谓的重置本质是不修改。一旦任务
        // 状态被修改,就意味着任务不可以再被执行了。
        return ran && s == NEW;
    }
  • private void handlePossibleCancellationInterrupt(int s) —— 处理可能的取消中断 —— 消除cancel()方法对任务执行线程造成的中断状态,避免对执行器的后续流程造成影响。cancel()方法的作用是取消任务,因此其造成的线程中断理论上只能作用于任务结束执行前的流程。而如果cancel()方法造成的线程中断状态在任务结束之后还继续保留,则可能会对后续的流程造成影响,因此需要将之还原。但问题在于程序允许调用者使用取消/中断的方式对任务的执行流程进行干预,例如将执行线程的中断状态作为某段特殊操作的标记位使用等,这种情况下的线程中断状态是不允许还原的。但由于无法区别执行线程的中断状态是cancel()方法造成还是其它操作造成的,因此注销了本该执行的Thread.interrupted()方法操作。因此,当前方法除了确保任务在被中断的情况下一定会在run方法中转变为INTERRUPTED(6:已中断)状态以外没有任何其它作用。
        方法会判断当前任务是否会被执行中断,即判断状态是否为INTERRUPTING(5:中断中)。如果需要,则不断的放弃CPU资源,直至状态被修改为INTERRUPTED(6:已中断)。
    /**
     * Ensures that any interrupt from a possible cancel(true) is only delivered to a task while in run or runAndReset.
     * 确保来自可能的 cancel(true)方法的任意中断只能在运行run()方法或runAndReset()方法时才能传递给任务。
     *
     * @Description: 名称:处理可能的取消中断
     * @Description: --------------------------------------------------------
     * @Description: 作用:消除cancel()方法对任务执行线程造成的中断状态,避免对执行器的后续流程造成影响。cancel()方法的作用是取消
     * @Description: 任务,因此其造成的线程中断理论上只能作用于任务结束执行前的流程。而如果cancel()方法造成的线程中断状态在任务结
     * @Description: 束之后还继续保留,则可能会对后续的流程造成影响,因此需要将之还原。但问题在于程序允许调用者使用取消/中断的方
     * @Description: 式对任务的执行流程进行干预,例如将执行线程的中断状态作为某段特殊操作的标记位使用等,这种情况下的线程中断状
     * @Description: 态是不允许还原的。但由于无法区别执行线程的中断状态是cancel()方法造成还是其它操作造成的,因此注销了本该执行的
     * @Description: Thread.interrupted()方法操作。因此,当前方法除了确保任务在被中断的情况下一定会在run方法中转变为INTERRUPTED
     * @Description: (6:已中断)状态以外没有任何其它作用。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法会判断当前任务是否会被执行中断,即判断状态是否为INTERRUPTING(5:中断中)。如果需要,则不断的放
     * @Description: 弃CPU资源,直至状态被修改为INTERRUPTED(6:已中断)。
     */
    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.
        // 我们的中断者可能有机会在打断我们之前暂停。让我们耐心等待。

        //     如果任务状态为INTERRUPTING(5:中断中),则不断的放弃CPU资源,直至状态被修改为INTERRUPTED(6:已中断)。这么做
        // 这么做的目的是确保cancel()方法造成的线程中断状态只限制在run()/runAndReset()方法中。只有当状态为INTERRUPTED(6:已中断)
        // 时,才确定cancel()方法已经执行了中断。
        //     cancel()方法的作用是取消任务,因此其造成的线程中断理论上只能作用于任务结束执行前的流程。而如果cancel()方法造成的线程
        // 中断状态在任务结束之后还继续保留,则可能会对后续的流程造成影响,因此需要将之还原。
        if (s == INTERRUPTING)
            while (state == INTERRUPTING)
                // wait out pending interrupt
                // 等到待定中断结束
                Thread.yield();

        // 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.
        // 我们需要清除任何我们可能从cancel()方法中接收的中断。但是,允许使用中断作为任务与调用者通信的独立机制,并且没有办法只
        // 清除取消中断。(这句话是指允许调用者使用取消/中断的方式对任务的执行流程进行干预,例如将执行线程的中断状态作为某段特
        // 殊操作的标记位使用等,这种情况下的线程中断状态是不允许还原的。但由于无法区别执行线程的中断状态是cancel()方法造成还是
        // 其它操作造成的,因此注销了本该执行的Thread.interrupted()方法操作)

        // Thread.interrupted();
    }
  • private void finishCompletion()—— 结束完成 —— 在当前任务结束之后(完成/异常/取消)唤醒等待当前任务执行结果的所有线程。

    方法首先会断开等待者链表与waiters(等待者链表)字段之间的引用,这是为了将任务结束前后的等待者区分开来。前者需要手动的唤醒,后者这不需要(具体逻辑看awaitDone()方法)。对于前者,遍历等待者链表,将其中所有的非空等待者唤醒,并断开与后继等待者的链接,这是为了帮助GC,避免跨带引用问题。而对于后者的产生,大致可以归纳为以下场景:

T1:线程A判断任务处于实际等待中/执行中状态,将自身封装为等待者;
T2:线程A判断任务依然处于实际等待中/执行中状态,准备加入等待者链表,但还未执行;
T3:线程B完成/异常/取消任务,触发对等待者的唤醒,waiters(等待者链表)字段被置null,并清理等待者外链;
T4:线程A将生成的等待者加入等待者内链。

    对于任务结束之后加入等待者内链的等待者,由于任务的状态已经被修改,因此对应的获取线程不会等待,而是会很快转换为空等待者,因此也就无需处理。而保留在等待者内链中的空等待者虽然属于垃圾残留,但也会随着未来任务一起被GC回收。

    /**
     * Removes and signals all waiting threads, invokes done(), and nulls out callable.
     * 移除和通知等待线程,调用done()方法,并且置空callable(可调用的)字段。
     *
     * @Description: 名称:结束完成
     * @Description: --------------------------------------------------------
     * @Description: 作用:在当前任务结束之后(完成/异常/取消)唤醒等待当前任务执行结果的所有线程。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法首先会断开等待者链表与waiters(等待者链表)字段之间的引用,这是为了将任务结束前后的等待者区分开
     * @Description: 来。前者需要手动的唤醒,后者这不需要(具体逻辑看awaitDone()方法)。对于前者,遍历等待者链表,将其中所有的
     * @Description: 非空等待者唤醒,并断开与后继等待者的链接,这是为了帮助GC,避免跨带引用问题。而对于后者的产生,大致可以归
     * @Description: 纳为以下场景:
     * @Description: T1:线程A判断任务处于实际等待中/执行中状态,将自身封装为等待者;
     * @Description: T2:线程A判断任务依然处于实际等待中/执行中状态,准备加入等待者链表,但还未执行;
     * @Description: T3:线程B完成/异常/取消任务,触发对等待者的唤醒,waiters(等待者链表)字段被置null,并清理等待者外链;
     * @Description: T4:线程A将生成的等待者加入等待者内链。
     * @Description: 对于任务结束之后加入等待者内链的等待者,由于任务的状态已经被修改,因此对应的获取线程不会等待,而是会很快
     * @Description: 转换为空等待者,因此也就无需处理。而保留在等待者内链中的空等待者虽然属于垃圾残留,但也会随着未来任务一起
     * @Description: 被GC回收。
     */
    private void finishCompletion() {
        // assert state > COMPLETING;
        // 断言状态 > COMPLETING(1:执行中)(说明该方法只在实际完成/异常/取消的情况下执行)。

        for (WaitNode q; (q = waiters) != null; ) {
            //     循环通过CAS操作将waiters(等待者链表)字段置为null,直至成功为止。这么做的目的是为了唤醒当前任务所有的等待者,
            // 但实际上即使在此处置null,等待者链表也还是可能出现存在等待者的情况,例如以下场景:
            //     T1:线程A判断任务处于实际等待中/执行中状态,将自身封装为等待者;
            //     T2:线程A判断任务依然处于实际等待中/执行中状态,准备加入等待者链表,但还未执行;
            //     T3:线程B完成/异常/取消任务,触发对等待者的唤醒,waiters(等待者链表)字段被置null,并清理等待者链表;
            //     T4:线程A将生成的等待者加入等待者链表。
            //     虽说会等待者链表会出现残留,但并不会出现该部分的线程被永久阻塞的情况。因为在等待者链表被置null之后加入的等待者
            // 自身并不会被挂起,而是会在下一轮循环中被设置为空等待者(具体查看awaitDone()方法注释),因此不会对整体流程造成影响。
            if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                // 死循环,目的是从头至尾遍历整个等待者外链,唤醒所有等待者线程。
                for (; ; ) {
                    //     判断是否是空等待者,如果不是,则将等待者设置为空等待者,并唤醒线程。等待者外链中的空等待者是因为等待者线
                    // 程被任务执行线程唤醒产生的,当然,也不排除存在少许中断/超时且为及时移除的空等待者。
                    Thread t = q.thread;
                    if (t != null) {
                        // 帮助GC。
                        q.thread = null;
                        LockSupport.unpark(t);
                    }
                    // 获取下个等待者,如果为null,表示已经全部遍历结束。
                    WaitNode next = q.next;
                    if (next == null)
                        break;
                    // unlink to help gc
                    // 断开链接以帮助GC(避免跨带引用)

                    // 断开当前等待者与后继等待者的链接以帮助GC,并将后继等待者设置为当前等待着以准备下个循环。
                    q.next = null;
                    q = next;
                }
                // 由于之前说过即使成功将waiters(等待者链表)字段置null,也可能会有新的等待者加入,因此可能会再次执行外循环(即因
                // 为waiters(等待者链表)字段不为null而导致for (WaitNode q; (q = waiters) != null; )循环再次执行)。为了避免这一点,在等
                // 待者链表的唤醒结束后,需要手动的终止。
                break;
            }
        }
        // 在等待者线程被唤醒之后执行自定义操作,用于子类实现重写,这是模板模式的一种使用。
        done();

        // to reduce footprint
        // 为了减少占用空间

        // 将callable(可调用的)字段置为空,表示已经执行完毕(但实际上可能还在执行,因为任务很可能因为不响应中断而继续执行)
        callable = null;
    }
  • private int awaitDone(boolean timed, long nanos) throws InterruptedException —— 等待结束 —— 获取当前任务的执行结果,如果任务正在执行则会等待,直至任务结束(完成/异常/取消)而获取到结果或抛出取消/执行异常。
        方法会先判断中断。如果获取线程已被中断则将等待者从等待者链表中移除并抛出中断异常;否则判断当前任务是否实际已结束(完成/异常/取消)。如果实际已结束(完成/异常/取消),则将等待者变为空等待者后返回状态值;否则判断当前任务是否实际处于的等待中/执行中。如果不是,说明当前任务实际已经执行完成/异常,但只是还没有执行结果/异常保存在未来中。这种情况下无需封装获取线程或令获取线程等待,因此在未来中保存结果/异常是非常快速的,只需暂时的放弃CPU资源等待结果/异常保存完成就可以了;而如果当前任务正在等待中/进行中,则就需要将获取线程有限/无限的等待。当然,在这之前需要先进经历将获取线程封装为等待者、将等待者通过CAS操作加入头节点等操作。
    /**
     * Awaits completion or aborts on interrupt or timeout.
     * 在中断或超时等待完成或中止
     *
     * @param timed true if use timed waits 如果使用等待等待则为ture
     * @param nanos time to wait, if timed 如果定时用于等待的时间
     * @return state upon completion 完成后的状态
     * @Description: 名称:等待结束
     * @Description: --------------------------------------------------------
     * @Description: 作用:获取当前任务的执行结果,如果任务正在执行则会等待,直至任务结束(完成/异常/取消)而获取到结果或抛出
     * @Description: 取消/执行异常。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法会先判断中断。如果获取线程已被中断则将等待者从等待者链表中移除并抛出中断异常;否则判断当前任务
     * @Description: 是否实际已结束(完成/异常/取消)。如果实际已结束(完成/异常/取消),则将等待者变为空等待者后返回状态值;
     * @Description: 否则判断当前任务是否实际处于的等待中/执行中。如果不是,说明当前任务实际已经执行完成/异常,但只是还没有执
     * @Description: 行结果/异常保存在未来中。这种情况下无需封装获取线程或令获取线程等待,因此在未来中保存结果/异常是非常快速
     * @Description: 的,只需暂时的放弃CPU资源等待结果/异常保存完成就可以了;而如果当前任务正在等待中/进行中,则就需要将获取
     * @Description: 线程有限/无限的等待。当然,在这之前需要先进经历将获取线程封装为等待者、将等待者通过CAS操作加入头节点等操
     * @Description: 作。
     */
    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) {
                //     如果状态(快照)> COMPLETING(1:执行中),则说明任务实际已经结束(完成/异常/取消),这意味着调用get()方法
                // 的线程不需要等待就可以获得结果/异常,此时如果已经生成了等待者,则将之设置为空等待者。并返回当前状态。
                //     此处并没有将等待者从等待者链表中移除,因为当任务实际结束(完成/异常/取消)时会全局清理整个等待者链表,因此
                // 不需要在此清理。但实际上全局清理也可能会有残留,比如在一个早已生成的等待者在任务实际结束(完成/异常/取消)之后
                // 才加入等待者链表...但这也不会造成什么影响,因为在全局清理开始之后再加入等待者链表的等待者并不会被挂起,因此也就
                // 不会出现某个等待者线程被永久阻塞的情况了。
                if (q != null)
                    q.thread = null;
                return s;
            } else if (s == COMPLETING)
                // cannot time out yet
                // 还未超时

                //     状态为COMPLETING(1:执行中)说明任务实际已经执行完成/异常了,但是还没有在未来中设置结果/异常。这种情况下
                // 完全无需将等待/获取线程封装为等待者在加入等待者链表,因此设置结果这一操作很快就能完成,因此只需直接暂时的方式
                // CPU资源即可。
                Thread.yield();
            else if (q == null)
                //     q == null说明当前任务实际正在等待/执行中,并且等待/获取线程是刚刚参与等待流程,因此会首先将之封装为等待者。
                q = new WaitNode();
            else if (!queued)
                //     !queued说明当前任务实际正在等待/执行中,并且等待/获取线程已被封装为等待者,但是还没有加入等待者链表中。没有
                // 加入等待者链表的原因是多元化的,可能是因为还未尝试加入,也可能是并发而导致的CAS加入失败,因此加入等待者链表可
                // 能会多次重复执行。代码中有一句相对特殊的代码q.next = waiters需要分两步判断:一是将q.next设置为waiters;二是将
                // q.next作为 参数。新加入的等待者会成为等待者链表的头节点。
                //     无论是入队成功还是失败,都会执行下一次循环而不是直接挂起线程,这是因为由于存在并发的运行,可能在入队时任务的
                // 状态已经发生了变化,因此需要再进行一次判断来选择下一步的操作。
                queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
            else if (timed) {
                //     timed说明当前任务实际正在等待/执行中,并且等待/获取线程已被封装为等待者并加入了等待者链表。接下来就需要令等
                // 待/获取线程进入限时等待状态。而在这之前会先判断是否已到达指定定时时间,如果是,则将等待者中等待者链表中移除并
                // 返回;否则另等待/获取线程等待
                nanos = deadline - System.nanoTime();
                if (nanos <= 0L) {
                    removeWaiter(q);
                    return state;
                }
                LockSupport.parkNanos(this, nanos);
            } else
                //     该情况说明任务处于等待状态,且线程已经加入等待者链表,但在调用get()方法时未设置超时时间。对于该情况则直接将
                // 线程挂起,直至唤醒/中断。
                LockSupport.park(this);
        }
    }
  • private void removeWaiter(WaitNode node) —— 移除等待者 —— 将中断/超时的等待者设置为空等待者,并遍历整个等待者链表移除所有的空等待者。

    方法首先会将当前等待者这是为空等待者,即将线程从等待者中移除,便表示其可被移除。随后会遍历等待者链表,将其中所有的空等待者全部移除。在这个遍历移除的过程中有两种情况会导致重遍历,即重新从头开始移除。比较简单的情况是等待者链表的头等待者是空节点,因为清理/新增多重并发的原因,将后继等待者设置为头等待者必须要使用CAS操作。如果CAS操作失败,就需要从头再来。这是不难理解的,因为本身操作点就在头部。另一种情况相对复杂,即如果前驱等待者与当前等待者都是空的等待者,就可能导致当前等待者在移除后被遗留在等待者链表中。具体如下:

T1:准备移除等待者q,等待者p为其前驱,等待者s为其后继;
T2:等待者p因为超时/中断而唤醒并成为空等待者,执行移除,等待者pp为前驱,等待者q为其后继;
T3:等待者p被移除,但依然持有等待者q的引用,此时等待者pp的后继等待者为q,由此等待者q此时实际有两个前驱;
T4:等待者q被移除,等待者p指向了等待者s,但等待者pp依然持有等待着q的引用,导致q未被移除。

    为了保证空等待者一定被移除,在上述情况下会触发重清理。这么做虽然会导致重遍历而导致性能损耗,但相比使用悲观锁等方案整体消耗要小的多,因为本身获取任务结果就并不是什么高并发场景,不然未来任务也不会使用乐观锁机制来保证线程安全。
    源码中的[retry:][continue retry]是一个较为少见的写法,可能很多同学都不知道其含义(包括我)。实际上,[retry:]的作用是标记一个重试起点,往往与for循环一起出现。当出现[continue retry]代码时,程序就会跳转到[retry:]处重新执行,类似于goto的作用。其中retry只是一个代号,并不是强制的关键字,事实上写成[again:]或者任意字符都是可以的,只是相对的[continue retry]也要变为[continue again]。

    /**
     * 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.
     * 尝试断开超时或中断等待节点的链接以避免累积垃圾。内部节点会不使用CAS简单的断开链接,因为它们通过释放器遍历任何都是无害
     * 的。为了避免已移除节点影响移除,列表会在明显竞争的情况下进行重遍历。当有很多节点时,这是很慢的,但我们不期望列表足够长,
     * 超过更高开销的方案。
     *
     * @Description: 名称:移除等待者
     * @Description: --------------------------------------------------------
     * @Description: 作用:将中断/超时的等待者设置为空等待者,并遍历整个等待者链表移除所有的空等待者。
     * @Description: --------------------------------------------------------
     * @Description: 逻辑:方法首先会将当前等待者这是为空等待者,即将线程从等待者中移除,便表示其可被移除。随后会遍历等待者链
     * @Description: 表,将其中所有的空等待者全部移除。在这个遍历移除的过程中有两种情况会导致重遍历,即重新从头开始移除。比较
     * @Description: 简单的情况是等待者链表的头等待者是空节点,因为清理/新增多重并发的原因,将后继等待者设置为头等待者必须要使
     * @Description: 用CAS操作。如果CAS操作失败,就需要从头再来。这是不难理解的,因为本身操作点就在头部。另一种情况相对复杂,
     * @Description: 即如果前驱等待者与当前等待者都是空的等待者,就可能导致当前等待者在移除后被遗留在等待者链表中。具体如下:
     * @Description: T1:准备移除等待者q,等待者p为其前驱,等待者s为其后继;
     * @Description: T2:等待者p因为超时/中断而唤醒并成为空等待者,执行移除,等待者pp为前驱,等待者q为其后继;
     * @Description: T3:等待者p被移除,但依然持有等待者q的引用,此时等待者pp的后继等待者为q,由此等待者q此时实际有两个前驱;
     * @Description: T4:等待者q被移除,等待者p指向了等待者s,但等待者pp依然持有等待着q的引用,导致q未被移除。
     * @Description: 为了保证空等待者一定被移除,在上述情况下会触发重清理。这么做虽然会导致重遍历而导致性能损耗,但相比使用悲
     * @Description: 观锁等方案整体消耗要小的多,因为本身获取任务结果就并不是什么高并发场景,不然未来任务也不会使用乐观锁机制
     * @Description: 来保证线程安全。
     * @Description: 源码中的[retry:][continue retry]是一个较为少见的写法,可能很多同学都不知道其含义(包括我)。实际上,[retry:]的
     * @Description: 作用是标记一个重试起点,往往与for循环一起出现。当出现[continue retry]代码时,程序就会跳转到[retry:]处重新执行,
     * @Description: 类似于goto的作用。其中retry只是一个代号,并不是强制的关键字,事实上写成[again:]或者任意字符都是可以的,只是
     * @Description: 相对的[continue retry]也要变为[continue again]。
     */
    private void removeWaiter(WaitNode node) {
        // 只有在等待者存在的情况下才需要移除。
        if (node != null) {
            // 设置等待者的线程为null,使其成为一个空等待者。
            node.thread = null;
            // 标记一个重试起点,当出现CAS竞争时,从此处进行重新开始循环。
            retry:
            for (; ; ) {
                // restart on removeWaiter race
                // 在removeWaiter()方法出现竞争是重新开始

                //     遍历整个等待者链表,则所有空等待者移除。在整个的等待者链表中,可能存在多个空等待者。原因是因为并发可能导致
                // 的其它线程超时/中断/唤醒。
                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;
                        //     在执行了对当前等待者的移除后,如果发现前驱等待者变为了空等待者,则重新从头清理等待者链表。从头清理的
                        // 原因是因为在这种情况下可能会导致当前等待者实际上并没有被移除,而是残留在等待者链表中。具体场景如下:
                        // T1:准备移除目标等待者q,其前驱等待者为p,后继等待者为s;
                        // T2:前驱等待者p因为超时/中断而唤醒,并将自身设置为空等待者,执行移除,其前驱等待者为pp,后继等待者为q;
                        // T3:前驱等待者p被移除,但依然持有的等待者q的引用,此时等待者pp的后继等待者为q,由此q实际有两个前驱等待者。
                        // T4:等待者q被移除,等待者p指向了等待者s,但等待者pp依然持有者等待着q的引用,导致q未被移除。
                        // 为了保证空等待者一定被移除,在上述情况下会触发重清理。这么做虽然会导致重遍历而导致性能损耗,但相比使用悲观
                        // 锁等方案整体消耗要小的多,因为本身获取任务结果就并不是什么高并发场景。
                        if (pred.thread == null)
                            // check for race
                            // 检查竞争
                            continue retry;
                    } else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, q, s))
                        //    该情况说明等待者链表中的第一个等待者就是空等待者,此时就需要通过CAS操作将后继等待者设置为头等待者。这里
                        // 之所以使用CAS操作一方面是因为清理可以并发,另一方面也是因为可能会有新的等待者加入。如果失败了则需要从头开
                        // 始重试。这里从头开始并不难理解,因为其本身就在头部操作。
                        continue retry;
                }
                break;
            }
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

说淑人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值