What‘s the FutureTask

327 阅读6分钟

What‘s the FutureTask

1.how do you think of async or sync

  • Before the talking about future, we should to know what's the async and sync, and their are how to work on multiThread。
1. sync: sequential way of working. on the working, task or method on single thread must execute / visit sequentially.
2. async: on the contrary of the sync.
  • 简而言之:
    1. 同步的思想是能够被大多数人接受的。在日常生活中,同步的方式就比如,A要炒鸡蛋西红柿。那么A首先需要准备打鸡蛋,接下来需要准备西红柿,有了鸡蛋和西红柿,A才能下锅炒菜。那么同步即需要等待上一事件的执行完毕,才可以执行当下的任务。
    2. 异步,这种不局限于同步思路的处理方式,主线程会将任务交给其他线程执行,在另一线程执行完毕后会通过回调/不回调来通知/不通知提交异步任务主线程/其他线程获取到线程的执行结果,异步的思路在于利用多线程的资源,同时不去阻塞主线程/当前线程的执行去执行计算/处理。
    3. 异步处理会消耗线程资源,并不是所有的任务都适合使用异步去处理,在项目中需要去评估哪些task需要被异步执行。

2. Future/FutureTask

  • Future 的思想我们可以理解为快递单号,那么FutureTask就类似于快递公司。当你需要寄快递的时候,你的包裹即为目前的task,那么在此同时快递公司会给你一个快递单号,来记录是这次这个task的。正常此时这个FutureTask就会执行内部的run()进行运输包裹. 之后你也可以通过打电话给快递公司来cancel()取消你的任务,或者是通过isDone()来查看快递是否被寄到了。当然,如果你此时想要拆快递,那么就必须等待快递被送到,所以你就会一直get()查询阻塞等待快递公司通知你,快递是否被送到。当然,你的其他朋友也知道这个快递单号的时候,他也可以通过去get()去查找这个快递是否寄到目的地(Task的只是一个结果,不要理解为任务本身) 贴图:

FutureTaskModel.png

  • FutureTask的状态转移图

FutureTaskRunning.png

  • FutureTask api

FutureTaskApi.png

3. code

  • Future解析

1. state Task任务状态:

 private volatile int state;
 private static final int NEW          = 0; // 初始化状态,在构造器的时候赋值
 private static final int COMPLETING   = 1; // task执行完成的时候
 private static final int NORMAL       = 2; // task执行完毕的最终状态 set方法中
 private static final int EXCEPTIONAL  = 3; // throw exception的时候
 private static final int CANCELLED    = 4; // 取消任务,在线程执行之前尝试阻止run,通过该表state mayInterruptIfRunning = false
 private static final int INTERRUPTING = 5; // only while interrupting the runner to satisfy a cancel(true) mayInterruptIfRunning = true
 private static final int INTERRUPTED  = 6; // be ibterrupted 中断
 
 //可能出现的几种状态转换
 * NEW -> COMPLETING -> NORMAL
 * NEW -> COMPLETING -> EXCEPTIONAL
 * NEW -> CANCELLED
 * NEW -> INTERRUPTING -> INTERRUPTED

2. cancel(boolean mayInterruptIfRunning)

public boolean cancel(boolean mayInterruptIfRunning) {
/**
在Task构造之后/run之前,尝试改变state状态,当mayInterruptIfRunning = true 为
INTERRUPTING false = CANCELLED。 
当然,一般情况下都是task执行了, state != New,那么当确切想中断运行任务,mayInterruptIfRunning = true 会使得获取当前线程进行中断标识,最后将state改变为INTERRUPTED。
最后执行 《finishCompletion》
*/
 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;
}

3. finishCompletion()

/**
 * Removes and signals all waiting threads, invokes done(), and
 * nulls out callable.
 * 这块是关键,无论cancel的 finally / get 的finally 都会执行该方法,这个方法的主要作用就是unpark waiters,
 * 关键在于, get() 维护了一个 WaitNode 来记录哪些线程调用了,当Task Completion 时,会唤醒那些被阻塞等待的线程。
 */
private void finishCompletion() {
    // assert state > COMPLETING;
    for (WaitNode q; (q = waiters) != null;) {
    //cas修改 waiters 的offset
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            for (;;) {
                Thread t = q.thread;
                if (t != null) {
                    q.thread = null;
                    //unpark 唤醒被阻塞的线程
                    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
}

4. get 阻塞获取异步任务处理的结果

//一直阻塞等待,知道结果返回或抛出异常
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    //如果s还没有执行完成
    if (s <= COMPLETING)
        // awaitDone
        s = awaitDone(false, 0L);
    return report(s);
}

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

//根据状态返回Task的结果值或者抛出异常
private V report(int s) throws ExecutionException {
    //outcome task result
    Object x = outcome;
    /** 这块为啥是NORMAL状态呢,因为NORMAL状态他表示当前任务的最终状态,
        在run中,当任务执行完成之后,会将state的 COMPLETING 改变为 NORMAL,
        可以说 NORMAL 是对外表示完成的状态,COMPLETING 则为对内完成的状态。
    */
    if (s == NORMAL)
    // 返回任务的结果值
        return (V)x;
        /** 如果是CANCELLED ,就是上面的cancel 为false的时候,会有该状态。
            这个时候获取的时候就会抛出异常 这块的状态可能会有 CANCELLED INTERRUPTING INTERRUPT
        */
    if (s >= CANCELLED)
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}


/**
 * 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
 * get的主要逻辑
 */
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    //计算出deadline,即等待执行时间,0为等待
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    //自旋
    for (;;) {
    //判断当前调用该方法的线程是否被中断,如果被中断,则将该线程移除waiters,这块是自旋,在break之前会一直检测
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        //当task的state为 NORMAL 之后,(如果之前这个线程被加入节点等待)就会将当前waiters中的q线程直接null,不必去操作Node内其他变量。(removeWaiters会help一下)同时返回结果s,s会进过report最终会返回给调用者结果
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        else if (s == COMPLETING) // cannot time out yet
        // cpu调度,当前调用该方法的线程让出cpu执行权
            Thread.yield();
        else if (q == null)
            q = new WaitNode();
            ///链表中新增waiters的节点
        else if (!queued)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
                                                 
        // 看看需不要处理等待时间的
        else if (timed) {
            //计算deadLine到当前时间的查询 deadLine = last.System.nanoTime() + nanos
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
            //超时了,需要将等待的节点剔除
                removeWaiter(q);
                return state;
            }
            //继续阻塞
            LockSupport.parkNanos(this, nanos);
        }
        else // s <= COMPLETING
            LockSupport.park(this);
    }
}


/**
 * 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变量置空
        node.thread = null;
        retry:
        //自旋
        for (;;) {          // restart on removeWaiter race
            // 挪掉节点中Thread为空的
            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;
        }
    }
}

5. run

/**
* task执行的方法
*/
public void run() {
//拿到当前执行run方法的线程, 如果状态不是NEW(这块在上面说到过,可以通过cancel阻止任务的执行)
    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);
    }
}


protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        //正如之前所说, NORMAL为最终的状态
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion();
    }
}

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)
    //之前说过,INTERRUPTING是当cancel(true),此时,线程设置中断标识,假如在此刻之前,就已经被中断了,就需要等待 finally -- UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);更改当前task的状态。这块check状态改变
        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();
}

4.总结

  • 这块可以通过总结一个时序图来加深对FutureTask的执行的流程。good bye