7
头图

Hello everyone, I'm Glacier~~

This article is a bit long, but it is full of dry goods. It analyzes two asynchronous models in the form of actual cases, and analyzes the Future interface and FutureTask class in depth from the perspective of source code. I believe you will gain a lot!

One, two asynchronous models

In Java's concurrent programming, there are generally two asynchronous programming models. One is to run other tasks in parallel in an asynchronous form without returning the result data of the task. One is to run other tasks asynchronously and need to return results.

1. Asynchronous model with no return result

For asynchronous tasks that do not return results, the task can be directly thrown into the thread or thread pool to run. At this time, the execution result data of the task cannot be directly obtained. One way is to use the callback method to obtain the running result of the task.

The specific solution is: define a callback interface, and define a method for receiving task result data in the interface, and the specific logic is completed in the implementation class of the callback interface. Put the callback interface together with the task parameters into the thread or thread pool to run, call the interface method after the task runs, and execute the logic in the callback interface implementation class to process the result data. Here, a simple example is given for reference.

  • Define the callback interface
package io.binghe.concurrent.lab04;

/**
 * @author binghe
 * @version 1.0.0
 * @description 定义回调接口
 */
public interface TaskCallable<T> {
    T callable(T t);
}

To facilitate the generic type of the interface, here a generic type is defined for the callback interface.

  • Defines the encapsulation class for task result data
package io.binghe.concurrent.lab04;

import java.io.Serializable;

/**
 * @author binghe
 * @version 1.0.0
 * @description 任务执行结果
 */
public class TaskResult implements Serializable {
    private static final long serialVersionUID = 8678277072402730062L;
    /**
     * 任务状态
     */
    private Integer taskStatus;

    /**
     * 任务消息
     */
    private String taskMessage;

    /**
     * 任务结果数据
     */
    private String taskResult;
    
    //省略getter和setter方法
    @Override
    public String toString() {
        return "TaskResult{" +
                "taskStatus=" + taskStatus +
                ", taskMessage='" + taskMessage + '\'' +
                ", taskResult='" + taskResult + '\'' +
                '}';
    }
}
  • Create an implementation class of the callback interface

The implementation class of the callback interface is mainly used to perform corresponding business processing on the returned result of the task. Here, for the convenience of demonstration, only the result data is returned. We need to do corresponding analysis and processing according to specific business scenarios.

package io.binghe.concurrent.lab04;

/**
 * @author binghe
 * @version 1.0.0
 * @description 回调函数的实现类
 */
public class TaskHandler implements TaskCallable<TaskResult> {
    @Override
public TaskResult callable(TaskResult taskResult) {
//TODO 拿到结果数据后进一步处理
    System.out.println(taskResult.toString());
        return taskResult;
    }
}
  • Create a task execution class

The execution class of the task is the class that specifically executes the task, implements the Runnable interface, defines a member variable of the callback interface type and a task parameter of the String type (parameters of the simulated task) in this class, and injects the callback interface and task parameters. The task is executed in the run method. After the task is completed, the result data of the task is encapsulated into a TaskResult object, and the method of the callback interface is called to pass the TaskResult object to the callback method.

package io.binghe.concurrent.lab04;

/**
 * @author binghe
 * @version 1.0.0
 * @description 任务执行类
 */
public class TaskExecutor implements Runnable{
    private TaskCallable<TaskResult> taskCallable;
    private String taskParameter;

    public TaskExecutor(TaskCallable<TaskResult> taskCallable, String taskParameter){
        this.taskCallable = taskCallable;
        this.taskParameter = taskParameter;
    }

    @Override
    public void run() {
        //TODO 一系列业务逻辑,将结果数据封装成TaskResult对象并返回
        TaskResult result = new TaskResult();
        result.setTaskStatus(1);
        result.setTaskMessage(this.taskParameter);
        result.setTaskResult("异步回调成功");
        taskCallable.callable(result);
    }
}

At this point, the entire big framework is completed, and the next step is to test whether the results of asynchronous tasks can be obtained.

  • Asynchronous task test class
package io.binghe.concurrent.lab04;

/**
 * @author binghe
 * @version 1.0.0
 * @description 测试回调
 */
public class TaskCallableTest {
    public static void main(String[] args){
        TaskCallable<TaskResult> taskCallable = new TaskHandler();
        TaskExecutor taskExecutor = new TaskExecutor(taskCallable, "测试回调任务");
        new Thread(taskExecutor).start();
    }
}

In the test class, use the Thread class to create a new thread and start the thread to run the task. The final interface data of the running program is shown below.

TaskResult{taskStatus=1, taskMessage='测试回调任务', taskResult='异步回调成功'}

You can savor this way of getting asynchronous results. Here, we simply use the Thread class to create and start threads, or we can use thread pools. You can implement your own implementation of obtaining asynchronous results through the callback interface in the form of a thread pool.

2. Asynchronous model with return result

Although it is possible to get the result of an asynchronous task using the callback interface, it is slightly more complicated to use. A processing solution that can directly return asynchronous results is provided in the JDK. The most commonly used is to use the Future interface or its implementation class FutureTask to receive the return result of the task.

  • Use the Future interface to get asynchronous results

Using the Future interface often cooperates with the thread pool to obtain asynchronous execution results, as shown below.

package io.binghe.concurrent.lab04;

import java.util.concurrent.*;

/**
 * @author binghe
 * @version 1.0.0
 * @description 测试Future获取异步结果
 */
public class FutureTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        Future<String> future = executorService.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "测试Future获取异步结果";
            }
        });
        System.out.println(future.get());
        executorService.shutdown();
    }
}

The running result is shown below.

测试Future获取异步结果
  • Use the FutureTask class to get asynchronous results

The FutureTask class can be used in combination with the Thread class or the thread pool. Next, let's look at these two ways of use.

An example of use in conjunction with the Thread class is shown below.

package io.binghe.concurrent.lab04;

import java.util.concurrent.*;

/**
 * @author binghe
 * @version 1.0.0
 * @description 测试FutureTask获取异步结果
 */
public class FutureTaskTest {

    public static void main(String[] args)throws ExecutionException, InterruptedException{
        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "测试FutureTask获取异步结果";
            }
        });
        new Thread(futureTask).start();
        System.out.println(futureTask.get());
    }
}

The running result is shown below.

测试FutureTask获取异步结果

An example of the use in conjunction with the thread pool is as follows.

package io.binghe.concurrent.lab04;

import java.util.concurrent.*;

/**
 * @author binghe
 * @version 1.0.0
 * @description 测试FutureTask获取异步结果
 */
public class FutureTaskTest {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                return "测试FutureTask获取异步结果";
            }
        });
        executorService.execute(futureTask);
        System.out.println(futureTask.get());
        executorService.shutdown();
    }
}

The running result is shown below.

测试FutureTask获取异步结果

It can be seen that using the Future interface or the FutureTask class to obtain asynchronous results is much simpler than using the callback interface to obtain asynchronous results. Note: There are many ways to achieve asynchronous, here is just an example of multi-threading.

Next, we will analyze the Future interface in depth.

2. In-depth analysis of the Future interface

1.Future interface

Future is a new asynchronous programming interface in JDK1.5, and its source code is as follows.

package java.util.concurrent;

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

As you can see, in the Future interface, a total of 5 abstract methods are defined. Next, we will introduce the meaning of these five methods respectively.

  • cancel(boolean)

Cancel the execution of the task, receive a boolean type parameter, if the task is successfully canceled, it will return true, otherwise it will return false. When the task has been completed, has ended or cannot be canceled for other reasons, the method will return false, indicating that the task has failed to cancel. When this method is called when the task is not started, and the result returns true (successfully canceled), the current task will no longer run. If the task has already been started, it will decide whether to interrupt the currently running thread to cancel the currently running task according to the currently passed boolean type parameter.

  • isCancelled()

Determines whether the task is canceled before completion, and returns true if it is canceled before the task is completed; otherwise, returns false.

There is one detail to pay attention to here: only if the task is not started, or is canceled before completion, it will return true, indicating that the task has been successfully canceled. All other cases will return false.

  • isDone()

Determine whether the task has been completed. If the task ends normally, throws an exception to exit, or is canceled, it will return true, indicating that the task has been completed.

  • get()

When the task is completed, return the result data of the task directly; when the task is not completed, wait for the task to complete and return the result data of the task.

  • get(long, TimeUnit)

When the task is completed, the result data of the task is directly returned; when the task is not completed, it waits for the task to complete, and sets the timeout waiting time. If the task is completed within the timeout period, the result will be returned; otherwise, a TimeoutException will be thrown.

2. RunnableFuture interface

The Future interface has an important sub-interface, which is the RunnableFuture interface. The RunnableFuture interface not only inherits the Future interface, but also inherits the java.lang.Runnable interface. Its source code is as follows.

package java.util.concurrent;

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

Here, ask, how many abstract methods are there in the RunnableFuture interface? Think about it! Hahaha. . .

This interface is relatively simple. The run() method is the method called when the task is run.

3. FutureTask class

The FutureTask class is a very important implementation class of the RunnableFuture interface. It implements all the methods of the RunnableFuture interface, the Future interface and the Runnable interface. There are many source codes of the FutureTask class, so I won't paste this one. You can check it under java.util.concurrent by yourself.

(1) Variables and constants in the FutureTask class

A state variable state is first defined in the FutureTask class. This variable is modified with the volatile keyword. Here, you only need to know that the volatile keyword achieves thread safety through memory barriers and reordering optimization. The volatile key will be separately analyzed in depth later. How words are guaranteed to be thread safe. Next, define several task runtime status constants, as shown below.

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;

Among them, several possible state change processes are given in the code comments, as shown below.

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

Next, several other member variables are defined as shown below.

private Callable<V> callable;
private Object outcome; 
private volatile Thread runner;
private volatile WaitNode waiters;

We saw the Callable interface that we are familiar with again. The Callable interface must be used to call the call() method to perform specific tasks.

  • outcome: Object type, indicating the result data or exception information obtained through the get() method.
  • Runner: Threads running Callable will use CAS to ensure thread safety during operation. Here you only need to know that CAS is a way for Java to ensure thread safety. In subsequent articles, we will deeply analyze how CAS ensures thread safety.
  • waiters: A variable of type WaitNode, representing the stack of waiting threads. In the implementation of FutureTask, the running status of the task will be exchanged through CAS combined with this stack.

Take a look at the definition of the WaitNode class as shown below.

static final class WaitNode {
    volatile Thread thread;
    volatile WaitNode next;
    WaitNode() { thread = Thread.currentThread(); }
}

As you can see, the WaitNode class is a static inner class of the FutureTask class, which defines a Thread member variable and a reference to the next WaitNode node. The thread variable is set to the current thread by the constructor.

(2) Construction method

Next, there are two construction methods of FutureTask, which are relatively simple, as shown below.

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

public FutureTask(Runnable runnable, V result) {
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;
}

(3) Whether to cancel and complete the method

Continue to look down at the source code and see a method for whether a task is canceled, and a method for whether a task is completed, as shown below.

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

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

In these two methods, whether the task has been cancelled and completed is determined by judging the status of the task. Why do you judge this way? Looking at the state constants defined in the FutureTask class again, it is found that the constants are defined regularly, not arbitrarily. Among them, the constants greater than or equal to CANCELLED are CANCELLED, INTERRUPTING and INTERRUPTED, all of which can indicate that the thread has been canceled. When the status is not equal to NEW, it can indicate that the task has been completed.

Through this, you can learn one thing: in the future, in the coding process, you must define the state you use according to the rules, especially when it involves frequent state changes in the business, the regular state can make business processing become You can get twice the result with half the effort, which can also be learned by looking at other people's source code design. Here, it is recommended that you look at the source code of excellent open source frameworks written by others.

(4) Cancellation method

We continue to look down at the source code. Next, we see the cancel(boolean) method, as shown below.

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    try {    // in case call to interrupt throws exception
        if (mayInterruptIfRunning) {
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally { // final state
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        finishCompletion();
    }
    return true;
}

Next, disassemble the cancel(boolean) method. In the cancel(boolean) method, first judge the status of the task and the operation result of CAS. If the status of the task is not equal to NEW or the operation of CAS returns false, it will directly return false, indicating that the task has failed to cancel. As follows.

if (!(state == NEW &&
      UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
          mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
    return false;

Next, in the try code block, first determine whether the thread where the current task is located can be interrupted to cancel the running of the task. If the thread where the current task is located can be interrupted, a Thread temporary variable is used to point to the thread running the task. When the pointed variable is not empty, the interrupt() method of the thread object is called to interrupt the running of the thread, and finally the thread is marked as interrupted state. As follows.

try {
    if (mayInterruptIfRunning) {
        try {
            Thread t = runner;
            if (t != null)
                t.interrupt();
        } finally { // final state
            UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
        }
    }
}

Here, it is found that the UNSAFE.putOrderedInt() method is used to change the task state. What the hell is this method? Click to take a look, as shown below.

public native void putOrderedInt(Object var1, long var2, int var4);

It can be seen that it is another local method, hey, leave it alone here, and follow-up articles will explain the functions of these methods in detail.

Next, the cancel(boolean) method will enter the finally block as shown below.

finally {
    finishCompletion();
}

You can see that the finishCompletion() method is called in the finally code block. As the name suggests, the finishCompletion() method indicates the end of the task. Let's see how it is implemented. Click on the finishCompletion() method to take a look, as shown below.

private void finishCompletion() {
    // assert state > COMPLETING;
    for (WaitNode q; (q = waiters) != null;) {
        if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
            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
}

In the finishCompletion() method, first define a for loop, the loop termination factor is waiters is null, in the loop, determine whether the CAS operation is successful, if the logic in the if condition is successful. First, define a for spin loop, in the spin loop body, wake up the thread in the WaitNode stack to make it run to completion. When the thread in the WaitNode stack finishes running, exit the outer for loop through break. Next call done() method. What the hell is the done() method? Click to take a look, as shown below.

protected void done() { }

As you can see, the done() method is an empty method body, which is handed over to subclasses to implement specific business logic.

When our specific business needs to execute some additional business logic when canceling the task, we can override the implementation of the done() method in the subclass.

(5) get() method

Continue to look down at the code of the FutureTask class, the FutureTask class implements two get() methods, as shown below.

public V get() throws InterruptedException, ExecutionException {
    int s = state;
    if (s <= 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);
}

The get() method without parameters will block until the task result is returned when the task is not completed. The get() method with parameters is that when the task is not completed and the waiting time exceeds the timeout time, a TimeoutException will be thrown.

The main logic of the two get() methods is similar, one has no timeout setting, and the other has a timeout setting. Let's talk about the main logic here. To determine whether the current state of the task is less than or equal to COMPLETING, that is, the task is in the NEW state or COMPLETING, call the awaitDone() method, and see the implementation of the awaitDone() method, as shown below.

private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    final long deadline = timed ? System.nanoTime() + nanos : 0L;
    WaitNode q = null;
    boolean queued = false;
    for (;;) {
        if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }

        int s = state;
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        else if (q == null)
            q = new WaitNode();
        else if (!queued)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        else if (timed) {
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos);
        }
        else
            LockSupport.park(this);
    }
}

Next, disassemble the awaitDone() method. In the awaitDone() method, the most important thing is the for spin loop. In the loop, first determine whether the current thread is interrupted. If it has been interrupted, call removeWaiter() to remove the current thread from the stack and throw InterruptedException exception, as shown below.

if (Thread.interrupted()) {
    removeWaiter(q);
    throw new InterruptedException();
}

Next, determine whether the current state of the task is completed, if it is completed, and the stack handle is not empty, set the current thread in the stack to be empty, and return the state of the current task, as shown below.

int s = state;
if (s > COMPLETING) {
    if (q != null)
        q.thread = null;
    return s;
}

When the status of the task is COMPLETING, make the current thread yield CPU resources, as shown below.

else if (s == COMPLETING)
    Thread.yield();

If the stack is empty, create a stack object as shown below.

else if (q == null)
    q = new WaitNode();

If the queue variable is false, assign a value to the queue through the CAS operation. If the timed parameter passed by the awaitDone() method is true, the timeout period is calculated. When the time has expired, the current thread is removed from the stack and the task status is returned, as shown below Show. If it does not time out, reset the timeout period as shown below.

else if (!queued)
    queued = UNSAFE.compareAndSwapObject(this, waitersOffset, q.next = waiters, q);
else if (timed) {
    nanos = deadline - System.nanoTime();
    if (nanos <= 0L) {
        removeWaiter(q);
        return state;
    }
    LockSupport.parkNanos(this, nanos);
}

If all the above conditions are not met, the current thread is set to wait state as shown below.

else
    LockSupport.park(this);

Next, go back to the get() method, when the awaitDone() method returns the result, or the task status does not meet the conditions, the report() method will be called, and the current task status will be passed to the report() method, and Return the result as shown below.

return report(s);

It seems that we have to look at the report() method here. Click in to see the implementation of the report() method, as shown below.

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

It can be seen that the implementation of the report() method is relatively simple. First, assign the outcome data to the x variable. Next, it is mainly to judge the received task status. If the status is NORMAL, convert x to a generic type and return it. ; When the status of the task is greater than or equal to CANCELLED, that is, the task has been cancelled, a CancellationException is thrown, and an ExecutionException is thrown in other cases.

At this point, the get() method analysis is completed. Note: Be sure to understand the implementation of the get() method, because the get() method is a frequently used method when we use the Future interface and the FutureTask class.

(6) set() method and setException() method

Continue to look at the code of the FutureTask class, the next thing you see is the set() method and the setException() method, as shown below.

protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion();
    }
}

protected void setException(Throwable t) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = t;
        UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
        finishCompletion();
    }
}

It can be seen from the source code that the overall logic of the set() method is almost the same as that of the setException() method, except that when setting the task state, one sets the state to NORMAL and the other sets the state to EXCEPTIONAL.

As for the finishCompletion() method, it has been analyzed before.

(7) run() method and runAndReset() method

Next, is the run() method. The source code of the run() method is as follows.

public void run() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        if (c != null && state == NEW) {
            V result;
            boolean ran;
            try {
                result = c.call();
                ran = true;
            } catch (Throwable ex) {
                result = null;
                ran = false;
                setException(ex);
            }
            if (ran)
                set(result);
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

It can be said that as long as Future and FutureTask are used, the run() method will inevitably be called to run the task. It is very necessary to master the process of the run() method. In the run() method, if the current state is not NEW, or the result returned by the CAS operation is false, it will return directly without executing the subsequent logic, as shown below.

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

Next, in the try code block, assign the member variable callable to a temporary variable c, determine that the temporary variable is not equal to null, and the task status is NEW, call the call() method of the Callable interface, and receive the result data. and set the ran variable to true. When the program throws an exception, the variable receiving the result is set to null, the ran variable is set to false, and the setException() method is called to set the status of the task to EXCEPTIONA. Next, if the ran variable is true, the set() method is called as shown below.

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

Next, the program will enter the finally block as shown below.

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

Here, runner is set to null, if the current state of the task is greater than or equal to INTERRUPTING, that is, the thread is interrupted. Then call the handlePossibleCancellationInterrupt() method. Next, look at the implementation of the handlePossibleCancellationInterrupt() method.

private void handlePossibleCancellationInterrupt(int s) {
    if (s == INTERRUPTING)
        while (state == INTERRUPTING)
            Thread.yield();
}

It can be seen that the implementation of the handlePossibleCancellationInterrupt() method is relatively simple. When the status of the task is INTERRUPTING, a while() loop is used, and the condition is that the current task status is INTERRUPTING, and the CPU resources occupied by the current thread are released, that is, when the task status is INTERRUPTING After running, release the resources occupied by the thread.

The logic of the runAndReset() method is similar to that of run(), except that the runAndReset() method resets the task state to NEW in the finally code block. The source code of the runAndReset() method is shown below, and the description will not be repeated.

protected boolean runAndReset() {
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread()))
        return false;
    boolean ran = false;
    int s = state;
    try {
        Callable<V> c = callable;
        if (c != null && s == NEW) {
            try {
                c.call(); // don't set result
                ran = true;
            } catch (Throwable ex) {
                setException(ex);
            }
        }
    } finally {
        // runner must be non-null until state is settled to
        // prevent concurrent calls to run()
        runner = null;
        // state must be re-read after nulling runner to prevent
        // leaked interrupts
        s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
    return ran && s == NEW;
}

(8) removeWaiter() method

The removeWaiter() method mainly uses the spin loop to remove the threads in the WaitNode, which is relatively simple, as shown below.

private void removeWaiter(WaitNode node) {
    if (node != null) {
        node.thread = null;
        retry:
        for (;;) {          // restart on removeWaiter race
            for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                s = q.next;
                if (q.thread != null)
                    pred = q;
                else if (pred != null) {
                    pred.next = s;
                    if (pred.thread == null) // check for race
                        continue retry;
                }
                else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                      q, s))
                    continue retry;
            }
            break;
        }
    }
}

Finally, at the end of the FutureTask class, there is the following code.

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

The function of these codes will be explained in detail in the subsequent in-depth analysis of CAS articles, and will not be discussed here.

So far, the source code of the Future interface and FutureTask class has been analyzed.

Okay, let's stop here today, I'm Glacier, see you in the next issue~~


冰河
156 声望970 粉丝