19

In Java, the Runnable interface represents a task that does not return a result, and the Callable interface represents a task that returns a result.
In concurrent programming, executing tasks asynchronously and then obtaining task results can improve the throughput of the system. The Future interface came into being. It represents the execution result of asynchronous tasks and provides functions such as checking whether the task has been executed, canceling the task, and obtaining the execution result of the task. FutureTask is the basic implementation of the Future interface and is often used in conjunction with the thread pool implementation class ThreadPoolExecutor.

This article is based on jdk1.8.0_91

1. Inheritance system

继承体系
The RunnableFuture interface implements both the Runnable interface and the Future interface, which is a redundant design.

java.util.concurrent.RunnableFuture

/**
 * A {@link Future} that is {@link Runnable}. Successful execution of
 * the {@code run} method causes completion of the {@code Future}
 * and allows access to its results.
 * 
 * @see FutureTask
 * @see Executor
 * @since 1.6
 * @author Doug Lea
 * @param <V> The result type returned by this Future's {@code get} method
 */
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

FutureTask is a cancelable asynchronous task. It is the basic implementation of the Future interface and has the following functions:

  • The execution of tasks initiated or interrupted;
  • Judge whether the task is completed;
  • Get the result after the task is executed.

At the same time, FutureTask can be used to wrap Callable or Runnable objects.
Since it implements the Runnable interface, it can be submitted to Executor for execution.

/**
 * A cancellable asynchronous computation. 
 *
 * @since 1.5
 * @author Doug Lea
 * @param <V> The result type returned by this FutureTask's {@code get} methods
 */
public class FutureTask<V> implements RunnableFuture<V>

java.util.concurrent.Executor

/**
 * An object that executes submitted {@link Runnable} tasks.
 *
 * @since 1.5
 * @author Doug Lea
 */
public interface Executor {

    void execute(Runnable command);
}

2. Properties

java.util.concurrent.FutureTask

// The run state of this task, initially NEW.
// 任务的执行状态,初始为 NEW。
private volatile int state;

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

Among them, the three attributes of state, runner, and waiters are contended during concurrency, and CAS is used to maintain their accuracy.

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

2.1 State definition

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

The state used in FutureTask represents the state of the task in the running process. With the execution of the task, the state will continue to change.

state Description :

  • NEW: Create a new state, and all tasks start from this state.
  • COMPLETING: The task result is being set (normal result or abnormal information).
  • NORMAL: The task is executed normally.
  • EXCEPTIONAL: An exception was thrown during the execution of the task.
  • CANCELLED: The task is cancelled (not responding to interrupt).
  • INTERRUPTING: The task is being interrupted.
  • INTERRUPTED: The task has been interrupted.

state transition process :

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

State classification :

  • The initial state of the task: NEW
  • Intermediate state of the task: COMPLETING, INTERRUPTING
  • The termination status of the task: NORMAL, EXCEPTIONAL, CANCELLED, INTERRUPTED

2.2 State use

In FutureTask, whether the task has been cancelled and completed is judged based on the state.

public boolean isCancelled() {
    return state >= CANCELLED; // CANCELLED、INTERRUPTING、INTERRUPTED
}

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

can be seen:

  • Cancelled or interrupted tasks (CANCELLED, INTERRUPTING, INTERRUPTED) are deemed to have been cancelled.
  • When the task leaves the initial state NEW, the task is deemed to have ended. The intermediate state of the task is very short, which does not mean that the task is being executed, but that the task has been executed and the final return result is being set.

According to the state value, FutureTask can guarantee that the completed task will not be run again or cancelled.

Although the intermediate state is a transient state, it is used for communication between threads in FutureTask. E.g:

  • If the status >= INTERRUPTING is detected in FutureTask#run, it means that other threads have initiated the cancellation operation, and the current thread needs to wait for the other party to complete the interruption.
  • If the status <= COMPLETING is detected in FutureTask#get, it means that the thread executing the task has not finished processing, and the current thread needs to wait for the other party to complete the task.

2.2 Treiber stack

/** Treiber stack of waiting threads */
private volatile WaitNode waiters; // 栈顶指针

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

FutureTask uses a linked list to construct a stack (Treiber stack, CAS is used to ensure the thread safety of stack operations, such as TransferStack in SynchronousQueue, please refer to my SynchronousQueue source reading notes ).
Among them, waiters is the head node of the linked list, which represents the pointer at the top of the stack.

stack effect :
FutureTask implements the Future interface. If the task is not completed when the result is obtained, the thread that obtains the result is suspended in the stack until the task is awakened.

3. Constructor

Assign task, set the initial state of the task.

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

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

Two places worth noting:

  • When the FutureTask is created, the status is NEW.
  • Since FutureTask uses Callable to represent tasks, the Executors#callable method needs to be used to convert Runnable to Callable.

test:

@Test
public void executors() throws Exception {
    Callable<String> callable = Executors.callable(new Runnable() {
        @Override
        public void run() {
            System.out.println("run!");
        }
    }, "haha");
    String call = callable.call();
    System.out.println("call = " + call);
}

Results of the:

run!
call = haha

4. Runnable implementation

4.1 FutureTask#run

Code flow:

  1. Check whether the task is executable: the task cannot be executed if the task has been executed or other threads have obtained execution rights.
  2. Call Callable#call to perform the task.
  3. If the task execution fails, use the setException method to set an exception.
  4. If the task is executed successfully, use the set method to set the return result.
  5. Finally, clear the record of the current thread and determine whether to wait for interruption.

Note that after the task execution ends, the attributes runner and callable will be cleared.

java.util.concurrent.FutureTask#run

public void run() {
    // state != NEW 说明任务已经执行完毕,不再重复执行
    // 将 runner 属性设置为当前线程,若设置失败说明其他线程已获取执行权
    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(); // 执行 Callable#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) // INTERRUPTING、INTERRUPTED
            handlePossibleCancellationInterrupt(s);
    }
}

4.1.1 FutureTask#set

After the task is successfully executed, call this method.
It is used to set the task status, set the task execution result, and wake up the threads in the stack waiting for the task execution result.

java.util.concurrent.FutureTask#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)) { // state: NEW -> COMPLETING
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state: COMPLETING -> NORMAL
        finishCompletion();
    }
}

status change: NEW -> COMPLETING -> NORMAL

Since the state attribute is volatile, putOrderedInt and putIntVolatile are equivalent here to ensure visibility.

Why does use lazySet instead of CAS:

  • In the case of concurrency, if only one thread executes CAS, changing the state from NEW to COMPLETING will succeed, and all other threads will fail.
  • Therefore, only one thread continues to modify the state to NORMAL, there is no contention, and no CAS is required.

4.1.2 FutureTask#setException

If an exception occurs in task execution, call this method.
Except for setting the task status, it is the same as FutureTask#set.

Status change: NEW -> COMPLETING -> EXCEPTIONAL

java.util.concurrent.FutureTask#setException

/**
 * 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)) { // state: NEW -> COMPLETING
        outcome = t;
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state: COMPLETING -> EXCEPTIONAL 
        finishCompletion();
    }
}

4.1.3 FutureTask#finishCompletion

After the execution is complete, the waiting thread is awakened.

java.util.concurrent.FutureTask#finishCompletion

/**
 * 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)) { 
        // CAS 将 waiters 属性置空:1. CAS 成功,遍历链表唤醒所有节点;2. CAS 失败,重新读取 waiters
            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
}

4.1.4 FutureTask#handlePossibleCancellationInterrupt

In the FutureTask#cancel method, the state is first set to INTERRUPTING, then the runner thread is interrupted, and finally the state is set to INTERRUPTED.

state == INTERRUPTING is checked in the finally block of FutureTask#run, it means that other threads have initiated the cancel(true) operation. Here you need to wait for other threads to interrupt the current thread. Until state != INTERRUPTING detected, it indicates that other threads have finished interrupting the current thread operation.

java.util.concurrent.FutureTask#handlePossibleCancellationInterrupt

/**
 * 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) // 其他线程中断当前线程之后,会设置 state 为 INTERRUPTED,使这里结束循环
            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.2 FutureTask#runAndReset

Support periodic execution of tasks:

  • If the task is executed successfully, there is no need to return the task result or change the task status (keep it as NEW), and the task can be executed again next time.
  • If the execution of the task fails, set the abnormal result and modify the task status (not NEW), and the task cannot be executed again next time.
  • To cancel the execution of the task, wait for other threads to interrupt the current thread, and modify the task state (not NEW), and the task cannot be executed again next time.
/**
 * 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); // 修改 state: NEW -> COMPLETING -> EXCEPTIONAL
            }
        }
    } 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; // 返回 true 则允许下次再执行 runAndReset
}

5. Future implementation

5.1 Future#get

Get the result of task execution:

  • If the task is not completed (NEW, COMPLETING), the thread that fetches the result will block (or spin).
  • If the task execution error (EXCEPTIONAL), throw an ExecutionException
  • If the task is cancelled (CANCELLED, INTERRUPTING, INTERRUPTED), throw a CancellationException
  • If the thread waiting is interrupted, throw InterruptedException

java.util.concurrent.FutureTask#get()

/**
 * @throws CancellationException {@inheritDoc}
 */
public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= COMPLETING)
        s = awaitDone(false, 0L); // 自旋或阻塞等待任务完成
    return report(s);             // 获取任务执行结果或抛出异常
}

5.1.1 FutureTask#awaitDone

Wait for the task to complete (task execution is completed, task execution is abnormal, task is canceled), if the current thread is interrupted or timed out, stop waiting.

Judge in spin:

  • If the current thread has been interrupted, the node will be popped from the stack and an InterruptedException will be thrown.
  • If state> COMPLETING, the task has been completed and return to the current state.
  • If state == COMPLETING, the task is about to be completed and the current thread continues to spin.
  • If state <COMPLETING, the current thread needs to be pushed onto the stack to wait:

    • No timeout, wait until it is awakened by other threads (FutureTask#run or FutureTask#cancel) or interrupted (Thread#interrupt);
    • There is a timeout period, blocking until it times out, is awakened, or interrupted. If it has timed out, pop the node out of the stack and return to state.

java.util.concurrent.FutureTask#awaitDone

/**
 * 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();   // 创建节点,设置 q.thread
        else if (!queued)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q); // 节点 q 入栈,作为新的头节点 waiters
        else if (timed) {
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);  // 已超时,将节点出栈
                return state;
            }
            LockSupport.parkNanos(this, nanos);
        }
        else
            LockSupport.park(this); // 进入阻塞,由 FutureTask#run 或 FutureTask#cancel 来唤醒(内部均调用 FutureTask#finishCompletion)
    }
}

5.1.2 FutureTask#report

The current thread waits to get the execution result of the task, or throws an exception.

java.util.concurrent.FutureTask#report

/**
 * 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) // CANCELLED、INTERRUPTING、INTERRUPTED
        throw new CancellationException();
    throw new ExecutionException((Throwable)x);
}

5.2 Future#get(timeout, unit)

Within a certain period of time, wait for the results of the task execution.

/**
 * @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(); // 等待超时了,任务还没有执行完,则抛出 TimeoutException
    return report(s);
}

5.3 Future#cancel

Try to cancel the execution of the task:

  • If the task is completed or cancelled, the cancel operation will fail and return false.
  • If the task has not been executed, the cancel operation will succeed and return true.
  • If the task is executing, the parameters of the method will indicate whether the thread needs to be interrupted:

    • mayInterruptIfRunning is true, the task currently being executed will be interrupted;
    • mayInterruptIfRunning is false, then the task being executed is allowed to continue running until it finishes executing.

status change:
NEW -> CANCELLED
NEW -> INTERRUPTING -> INTERRUPTED

public boolean cancel(boolean mayInterruptIfRunning) {
    // 如果任务还没有启动(NEW),则修改任务状态(INTERRUPTING or CANCELLED),修改成功则进入下一步
    // 如果任务状态不是 NEW,则直接返回。说明任务已完结(已完成、已取消、出现异常),无法取消,返回 false
    if (!(state == NEW &&
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception 
        // 进入这里,说明任务状态为 INTERRUPTING or CANCELLED
        // mayInterruptIfRunning 为 true 说明需要中断执行任务的线程,为 false 允许任务继续执行完
        if (mayInterruptIfRunning) { 
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                // 只有一个线程会执行到这里,无需使用 CAS
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED); // INTERRUPTING -> INTERRUPTED 
            }
        }
    } finally {
        finishCompletion(); // 唤醒等待线程
    }
    return true;
}

6. Examples

Use three threads to execute sequentially: submit a task, wait for a task, and cancel a task.
Observe the execution results and understand how to use Future to interact between multiple threads under concurrent conditions.

/**
 * 三个线程依次执行:提交任务、等待任务、取消任务
 * 在任务未执行完的时候,取消任务。
 * 
 * @author Sumkor
 * @since 2021/4/28
 */
@Test
public void cancel() throws InterruptedException {
    // 定义任务
    FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
        @Override
        public String call() throws Exception {
            Thread.sleep(10000);
            return "哦豁";
        }
    });

    CountDownLatch submitGate = new CountDownLatch(1); // 等待任务提交
    CountDownLatch endGate = new CountDownLatch(3);    // 等待线程执行完

    // 提交任务
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                submitGate.countDown();

                System.out.println(Thread.currentThread().getName() + " 执行任务开始");
                futureTask.run();
                System.out.println(Thread.currentThread().getName() + " 执行任务结束");
            } finally {
                endGate.countDown();
            }
        }
    }).start();

    // 等待任务
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                submitGate.await();
                Thread.sleep(1000);// 等待 futureTask.run() 执行一段时间后再获取结果

                System.out.println(Thread.currentThread().getName() + " 获取任务结果开始");
                String result = futureTask.get();
                System.out.println(Thread.currentThread().getName() + " 获取任务结果结束 " + result);
            } catch (Exception e) {
                System.out.println(Thread.currentThread().getName() + " 获取任务结果失败 " + e.getMessage());
                e.printStackTrace();
            } finally {
                endGate.countDown();
            }
        }
    }).start();

    // 取消任务
    new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                submitGate.await();
                Thread.sleep(2000);// 等待 futureTask.get() 执行一段时间后再取消任务

                System.out.println(Thread.currentThread().getName() + " 取消任务开始");
                boolean cancel = futureTask.cancel(true);
                System.out.println(Thread.currentThread().getName() + " 取消任务结束 " + cancel);
            } catch (Exception e) {
                System.out.println(Thread.currentThread().getName() + " 取消任务失败 " + e.getMessage());
                e.printStackTrace();
            } finally {
                endGate.countDown();
            }
        }
    }).start();

    endGate.await();
}

Results of the:

Thread-0 执行任务开始
Thread-1 获取任务结果开始
Thread-2 取消任务开始
Thread-2 取消任务结束 true
Thread-0 执行任务结束
Thread-1 获取任务结果失败 null
java.util.concurrent.CancellationException
    at java.util.concurrent.FutureTask.report(FutureTask.java:121)
    at java.util.concurrent.FutureTask.get(FutureTask.java:192)
    at com.sumkor.pool.FutureTest$6.run(FutureTest.java:129)
    at java.lang.Thread.run(Thread.java:745)

Description:

  • After thread A starts the task for a period of time, thread B gets the task result and enters the wait.
  • Then thread C cancels the task and interrupts thread A (thread A will not throw an exception, because FutureTask#cancel modified the state first, causing the CAS in FutureTask#setException to fail).
  • At this time, thread B is awakened while waiting (woke up by thread C, and the state is checked to be INTERRUPTED) and throws an exception CancellationException.

7. Summary

  • FutureTask implements the Runnable and Future interfaces and is a cancelable asynchronous task.
  • The task in FutureTask has 7 states, and multiple threads use this state to operate the task, such as judging whether the task has been completed, canceling the task, and obtaining the task result.
  • In FutureTask, as long as the task is not in the NEW state, it means that the task has been executed or is no longer executed, and it does not indicate the state of "task being executed".
  • FutureTask uses linked list and CAS mechanism to build a concurrent and safe stack for storing threads waiting to obtain task results.
  • When FutureTask is waiting to get the task result, it will still block the main thread, which violates the original intention of asynchrony. JDK 8 introduced CompletableFuture, which uses a callback mechanism to obtain task results asynchronously.

Author: Sumkor
Link: https://segmentfault.com/a/1190000039943509


Sumkor
148 声望1.3k 粉丝

会写点代码