JUC source code analysis - thread pool Part (b) FutureTask

JUC source code analysis - thread pool Part (b) FutureTask

JDK5 provides Callable and Future interfaces later, you can get the results of the task after the task is completed by them. Herein specific implementation principle from the perspective of the source code.

1. Introduction Interface

FutureTask class structure

1.1 Callable Interface

For tasks that are required to achieve Callable Interface, Callable interface definition is as follows:

public interface Callable<V> {
    V call() throws Exception;
}

Callable can see a generic interface, generic V is to call (the return type of the method). Callable and Runnable interface like the interface, can be executed by another thread, but as said earlier, Runnable not return data can not throw an exception.

1.2 Future Interface

Future interface represents the result of an asynchronous computation, you can check whether asynchronous computation done by the method Future interface provided, or wait for the results and get the results, but also can cancel the execution. Future interface is defined as follows:

public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException, ExecutionException;
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}
  • the Cancel () : used to cancel the execution of asynchronous tasks. If the asynchronous task has been completed or has been canceled, or for some reason can not be canceled, it returns false. If the task has not been executed, it returns true and asynchronous task will not be executed. If the task has already started but not yet implemented a complete, if mayInterruptIfRunning is true, it will immediately interrupt threads to perform tasks and return true if mayInterruptIfRunning is false, it returns true and does not interrupt task execution thread.
  • isCanceled () : determine whether the task is canceled, if the task was canceled before the end (the end of normal execution or execution abnormal end) returns true, otherwise returns false.
  • isDone () : determine whether the task has been completed, it returns true if completed, otherwise returns false. Note that: exception occurred during the execution of the task, the task was canceled also belong to the tasks have been completed, it will return true.
  • GET () : Gets the task execution results, if the task is not yet complete will block waiting until the completion of the task execution. If a task is canceled will be thrown CancellationException exception if an exception occurs during the execution of the task will be thrown ExecutionException exception if the process is interrupted block waiting InterruptedException will be thrown exception.
  • GET (Long timeout, Timeunit Unit) : GET band timeout () version, if the process is blocked waiting for the Super League will be thrown TimeoutException exception.

1.3 FutureTask

Future just an interface, can not be directly used to create objects, it is FutureTask Future implementation class, structure FutureTask follows:

FutureTask -> FutureRunnable -> Future/Runnable 

Can be seen that, to achieve a FutureTask RunnableFuture interface, the interface extends RunnableFuture Runnable interface and Future interface, so not only as a Runnable FutureTask Thread executed directly, can also be used to obtain the results as Future of Callable.

2. FutureTask source code analysis

2.1 FutureTask life cycle

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;  // 对执行任务的线程进行中断(未必中断到)

FIG FutureTask state changes:

FutureTask state

  • NEW : represents a new task or tasks to be performed has not finished. This is the initial state.
  • The COMPLETING : the task has been completed or executed when an exception occurs to perform tasks, but the task execution results or unconventional outcome has not been saved to the field (outcome field is used to save the task execution results, if an exception occurs, the reason for the exception to save) time , the state will change from NEW to COMPLETING. But this time the state will be relatively short, is an intermediate state.
  • The NORMAL : the task has been executed and completed the task execution results are saved to the outcome field, the state will transition from COMPLETING to NORMAL. This is a final state.
  • EXCEPTIONAL : After the task execution exception occurs and the cause of the abnormality has been saved to the outcome field, the state will transition from COMPLETING to EXCEPTIONAL. This is a final state.
  • CANCELLED : the task has not started or has started but not yet completed execution time, the user calls the cancel (false) method to cancel the task and do not interrupt task execution thread, this time from the state would be converted into CANCELLED NEW state. This is a final state.
  • INTERRUPTING : the task has not started but has not yet been performed or when the execution is completed, the user calls cancel (true) method to cancel the task and interrupt task execution thread but the thread has not executed before the task is interrupted, the state would be converted to INTERRUPTING from NEW . This is an intermediate state.
  • INTERRUPTED : call interrupt () interrupts after task execution thread state is converted from INTERRUPTING to INTERRUPTED. This is a final state.

NEW can be seen as a starting state, NORMAL, EXCEPTIONAL, CANCELLED, INTERRUPTED state to terminate these states, while temporarily COMPLETING and INTERRUPTING intermediate state. Why introduce COMPLETING and INTERRUPTING two intermediate state? COMPLETING - middle> INTERRUPTED outcome = v have the assignment process, INTERRUPTING -> INTERRUPTED have t.interrupt process. This process is to ensure that no other thread interference. (Personal understanding)

One thing to note is that all states have a value greater than COMPLETING indicates that the task has been executed completed (tasks normally executed, task or tasks to perform anomaly is canceled).

2.2 Internal Structure

// 内部封装的 Callable 对象。如果是 Runnable 则会通过 Executors#callable 封装成 Callable
private Callable<V> callable;
private Object outcome;             // 用于保存计算结果或者异常信息。
private volatile Thread runner;     // 用来运行 Callable 的线程
private volatile WaitNode waiters;  // FutureTask中用了 Trieber Stack 来保存等待的线程
                                    // 这个队列使用 Treiber stack(可以理解为基于 CAS 的无锁的栈,先进后出)

FutureTask with the Treiber Stack to maintain the thread waits for completion of the task, task completion FutureTask / cancel / back abnormal wakes the thread stack waiting in finishCompletion hook method.

Constructor 2.3

Start with the constructor of FutureTask looks:

public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

Can be seen, waiters initial value is null, the initial state NEW.

General launched next task threads with a thread to perform tasks usually will not be the same thread, the time to perform the task, initiating thread can review the task execution status in the task execution thread (usually a thread pool) to obtain the results of task execution, cancel the task etc. operations, then analyze these operations.

2.4 task execution thread

2.4.1 Core method run

After executor.submit (task) will be submitted to the thread pool task, no doubt FutureTask will run the original packaging method, when the run method executes complete the setup result values, and wake up all waiting threads.

/**
 * run 方法执行有两个条件:1. state=NEW; 2. runner=null
 * 1. 执行前 state=NEW & runner=null
 * 2. 执行中 state=NEW & runner=Thread.currentThread()
 * 3. 执行后 state!=NEW & runner=null,根据是否有异常执行 set(result) 或 setException(ex),
 *    set/setException 都会更新 state 状态,之后线程的状态就不是 NEW
 * 因此,多个线程同时调用 run 方法的情况 callable 也只会执行一次
 */
public void run() {
    // 1. state为 NEW 且对 runner 变量 CAS 成功。 对 state 的判断写在前面,是一种优化。 
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            // 2. 是否成功运行的标志位,而不是把 set 方法写在 try 中,是为了不想捕获 set 的异常。
            //    比如:子类覆写 FutureTask#done 方法(set->finishCompletion >done) 抛出异常,
            //    然而实际上提交的任务是有正常的结果的。
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // 3. 如果A/B两个线程同时执行到步骤①,当A线程重新将runner设置为 null 时,B线程是否可能会重新执行呢?
        //    实际上A线程一旦已经调用 set/setException 方法,整个流程已经结束了,所以不可能重复执行
        runner = null;

        // 4. 等待调用 cancel(true) 的线程完成中断,防止中断操作逃逸出 run 或者 runAndReset 方法
        //    以至于线程在执行后续操作时被中断
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

Summary: RUN method is callable be a layer of packaging, returns results after the execution is complete, and wake up all waiting threads.

  1. Determine the current state of the task is not NEW then the task or have been performed, or has been canceled, direct return.
  2. If the status is NEW then followed by unsafe class will be the task execution thread references stored in CAS runner field, there is one and only one thread can be successful, equivalent to an exclusive lock.
  3. If the task execution is completed, the call set or setException method, and wake up all waiting threads.

2.4.2 Set result set / setException

// 设置返回结果,唤醒等待线程。思考为什么需要 COMPLETING 这个中间状态
protected void set(V v) {
    // 可能任务已经取消,state 的状态就不再是 NEW
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion();
    }
}

According to the previous analysis, whether it is abnormal or task to perform tasks normally executed, or cancel the task, and finally calls the finishCompletion () method, the method is implemented as follows:

private void finishCompletion() {
    // assert state > COMPLETING;
    for (WaitNode q; (q = waiters) != null;) {
        // 1. 必须将栈顶 CAS 为 null,否则重读栈顶并重试。
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            // 2. 遍历并唤醒栈中节点对应的线程。
            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;
        }
    }

    // 3. ExecutorCompletionService#QueueingFuture 中把结果加到阻塞队列里。
    //    CompletionService 谁用谁知道,奥秘全在这。
    done();     // 子类覆盖

    // 4. callable 置为 null 主要为了减少内存开销,更多可以了解 JVM memory footprint 相关资料
    callable = null;        // to reduce footprint
}

Implementation of this method is relatively simple, waiters, traversing the linked list, the thread wakes node, then callable empty. The thread will be awakened each () returns from blocking awaitDone () method LockSupport.park, and then will conduct a new round of circulation. In the new round of the loop will return the results or, more precisely, is the return status of the task.

2.5 task calling thread

2.5.1 obtain the results get

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

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

Summary: If the task has not been executed on the end of the current thread blocked calls awaitDone end here includes normal tasks completed, abnormal task execution, the task was canceled. Once the task execution ends the call report () returns the result.

// report 根据任务最终的状态,返回结果或抛出异常
private V report(int s) throws ExecutionException {
    Object x = outcome;
    if (s == NORMAL)    // 1. 正常执行完计算任务
        return (V)x;
    if (s >= CANCELLED) // 2. 取消
        throw new CancellationException();
    throw new ExecutionException((Throwable)x); // 3. 异常
}

2.5.2 wait for end of the mission awaitDone

When you call get () to obtain the results of the task but the task has not completed execution time, the calling thread will call awaitDone () method is blocked waiting for, which is defined as follows:

// 阻塞等待线程执行完成(正常、异常、取消)后返回
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    for (;;) {
        // 1. 调用 get 的线程是否被其他线程中断
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        // 2. 任务已经执行完成,无论是正常、异常、取消
        if (s > COMPLETING) {   // 已经执行完成
            if (q != null)      // help GC
                q.thread = null;
            return s;
        // 3. 结果正在赋值,让出 CPU 等待
        } else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        // 4. 初始化节点,重试一次循环
        else if (q == null)
            q = new WaitNode();
        // 5. queued 记录是否已经入栈,此处准备将节点压栈
        //    节点入队失败,自旋直至成功
        else if (!queued)
            // 这是 Treiber Stack算法入栈的逻辑。 Treiber Stack 是一个基于 CAS 的无锁并发栈实现
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
        // 6. 挂起线程
        else if (timed) {
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);    // 超时直接删除节点后返回
                return state;
            }
            LockSupport.parkNanos(this, nanos);
        }
        else
            LockSupport.park(this); // 只有任务取消(cancel)和任务执行结束(run),才能唤醒线程
                                    // 此时再一次for循环时state的状态肯定大于COMPLETING
    }
}

Summary: awaitDone the following situations:

  1. Whether the call to get the thread is interrupted by other threads, direct throw InterruptedException.
  2. Return result end of the mission, the result here is the status of the task. As long as it means greater than COMPLETING End Task.
  3. Call to get into the team's spin thread, and the thread hangs waiting wake. Only finishCompletion will wake up the thread, but this method only task cancel cancel or end run task execution method will be called. Of course, if the task timeout, direct return, this status may be NEW.

2.5.3 delete nodes removeWaiter

/**
 * 清理用于保存等待线程栈里的无效节点,所谓节点无效就是内部的 thread 为 null(类比 ThreadLocalMap)
 *   
 * 一般有以下几种情况:
 * 1. 节点调用 get 超时。
 * 2. 节点调用 get 中断。
 * 3. 节点调用 get 拿到 task 的状态值(> COMPLETING)。
 *
 * 此方法干了两件事情:
 * 1. 置标记参数 node 的 thread 为 null
 * 2. 清理栈中的无效节点
 *
 * 如果在遍历过程中发现有竞争则重新遍历栈。
 */
private void removeWaiter(WaitNode node) {
    if (node != null) {
        node.thread = null;
        retry:
        for (;;) {          // restart on removeWaiter race
            // pre(前继节点) -> current(当前节点) -> next(后继节点),对应下面的 pre -> q -> s
            // 1.1 q.thread!=null 则更新 pre 节点继续遍历
            // 1.2 pre!=null && q.thread==null 时 current 不时头节点,直接删除 current
            // 1.3 pre==null && q.thread==null 时 current 是头节点,更新头节点为 next
            for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                s = q.next;
                // 1.1 保存当前节点 q 的前继节点 pre
                if (q.thread != null)
                    pred = q;
                // 1.2 q 节点已经失效,此时根据 q 是否是头节点分两种情况
                //     q 不是头节点,直接删除,为什么不需要原子性操作?
                else if (pred != null) {
                    pred.next = s;              // 踢除当前节点 q
                    if (pred.thread == null)    // check for race
                        continue retry;
                // 1.3 q 是头节点且失效了,原子性更新头节点
                } else if (!UNSAFE.compareAndSwapObject(this, waitersOffset, q, s))
                    continue retry;
            }
            break;
        }
    }
}

2.5.4 cancel a task cancel

/**
 * mayInterruptIfRunning:false 时不允许在线程运行时中断,true 时允许运行时中断线程,但不保证一定会中断线程。
 * 1. true 时,将状态修改成 INTERRUPTING,执行 thread.interrupt()
 * 2. false 时,将状态修改成 CANCELLED
 */
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();  // interrupt 不一定能中断线程
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion(); // 唤醒所有等待线程
    }
    return true;
}

Summary: the Cancel method will do the following things:

  1. Modify the status of the task. Note that the parameter name called mayInterruptIfRunning since then the task may not be able to stop by the interrupt. Business depends on whether the thread in response to interrupt. Such as the following code while conditions were changed to true, call the cancel simply can not interrupt task.

    // !Thread.interrupted() 改为 true 后无法中断任务
    Future<?> future = executorService.submit(() -> { 
        while (!Thread.interrupted()) System.out.println("1"); 
    });
    future.cancel(true);
  2. Wake-up call finishCompletion all waiting for results of the task threads.

reference:

  1. This article is reproduced to "in-depth study a FutureTask,": http://www.importnew.com/25286.html
  2. "FutureTask Source Interpretation": https://www.cnblogs.com/micrari/p/7374513.html

The intentions of recording a little bit every day. Perhaps the content is not important, but the habit is very important!

Guess you like

Origin www.cnblogs.com/binarylei/p/10958885.html