[Java concurrent programming] Java multithreading (two): Callable&Future&FutureTask source code analysis

1. Create thread task scheme one: Runnable

runnable has no return value, run implements thread logic

public interface Runnable {
    
    
    public abstract void run();
}

2. Create thread task program two: Callable

2.1 Callable

callable has a return value (V), call implements thread logic

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

2.2 Future

Callabe cannot be used alone. Future is needed to control Callable execution and obtain Callable execution result.

public interface Future<V> {
    
    
    // 如果任务已经成功了,或已经取消了,是无法再取消的,会直接返回取消成功(true)
    // 如果任务还没有开始进行时,发起取消,是可以取消成功的。
    // 如果取消时,任务已经在运行了,mayInterruptIfRunning 为 true 的话,就可以打断运行中的线程
    // mayInterruptIfRunning 为 false,表示不能打断直接返回
    boolean cancel(boolean mayInterruptIfRunning);
	
    // 返回线程是否已经被取消了,true 表示已经被取消了
    // 如果线程已经运行结束了,isCancelled 和 isDone 返回的都是 true
    boolean isCancelled();

    // 线程是否已经运行结束了
    boolean isDone();

    // 等待结果返回
    // 如果任务被取消了,抛 CancellationException 异常
    // 如果等待过程中被打断了,抛 InterruptedException 异常
    V get() throws InterruptedException, ExecutionException;

    // 等待,但是带有超时时间的,如果超时时间外仍然没有响应,抛 TimeoutException 异常
    V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
}
  • The main function of the get method is to get the result of the execution of the Callable asynchronous task. The get without parameters will wait for the task execution to complete before returning. The get method with parameters can set a fixed time. Within the set time, if the task has not been executed successfully , Return the exception directly. In actual work, it is recommended to use the get parameter method more and the get parameterless method less to prevent the task execution is too slow, most threads are waiting, causing the problem of thread exhaustion.

  • The cancel method is mainly used to cancel the task. If the task has not been executed, it can be canceled. If the task is already in the process of execution, you can choose not to cancel, or directly interrupt the task in execution.

Then there is a problem here. Callable and Future are both interfaces. How to control Callable through Future? You can create an intermediate class to implement the Future interface, then combine the Callable, and finally achieve control through the methods in the Future interface. The specific logic here can be seen in FutureTask.

3.FutureTask

Both Runnable and Callable can represent the tasks to be performed by the thread, so how can these two interfaces be transformed into each other on the basis that they should not be related?

  • First of all, Runnable and Callable must not be implemented by extends, because direct inheritance is the is-a relationship, and these two are obviously not
  • Then there is another way to introduce an intermediate class F (satisfying the following two conditions)
    • F extends Runnable:继承Runnable
    • F { primary Callable c }:组合Callable

==> Finally, combining all the above analysis, we get the structure of FutureTask: implement Future interface & implement Runnable interface & combine Callable

public class FutureTask<V> implements RunnableFuture<V> {
    
    
	// 任务状态
    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;//任务被打断成功

    // 组合了 Callable 
    private Callable<V> callable;
    
    // 异步线程返回的结果
    private Object outcome; 
    // 当前任务所运行的线程
    private volatile Thread runner;
    // 记录调用 get 方法时被等待的线程
    private volatile WaitNode waiters;
    
    //---------------------------构造方法---------------------------------
    // 构造器中传入Callable接口实现对象,对callable成员变量进行初始化
    public FutureTask(Callable<V> callable) {
    
    
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        // 任务状态初始化
        this.state = NEW;       // ensure visibility of callable
    }

    // 使用 Runnable 初始化,并传入 result 作为返回结果。
    // Runnable 是没有返回值的,所以 result 一般没有用,置为 null 就好了
    public FutureTask(Runnable runnable, V result) {
    
    
        // Executors.callable 方法把 runnable 适配成 RunnableAdapter,
        // RunnableAdapter 实现了callable,所以也就是把 runnable 直接适配成了 callable。
        this.callable = Executors.callable(runnable, result);
        this.state = NEW;       // ensure visibility of callable
    }
}

3.1 RunnableFuture

First look at what the RunnableFuture interface implemented by FutureTask is

  • Inherit Runable: Turn the outside into Runnable
  • Inherit Future: realize Future's control of Callable
public interface RunnableFuture<V> extends Runnable, Future<V> {
    
    
    void run();
}

Let's take a look at how FutureTask implements the methods in the Future interface and Runnable interface.

3.2 Implementation of Future interface method

  • Through the realization of Future, the combination of Callable realizes the combination of the two
    • Constructor passes in Callable
    • Implement the Future method to return the result operation
  • FutureTask implements Runnable, when the constructor is passed to Callable, it is directly converted to Runnable

get

get has two methods: infinite blocking and with timeout. We usually recommend the method with timeout. The source code is as follows

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

// 等待任务执行完成
private int awaitDone(boolean timed, long nanos)
    throws InterruptedException {
    
    
    // 计算等待终止时间,如果一直等待的话,终止时间为 0
    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;
        }
        // 如果正在执行,当前线程让出 cpu,重新竞争,防止 cpu 飙高
        else if (s == COMPLETING) // cannot time out yet
            Thread.yield();
        // 如果第一次运行,新建 waitNode,当前线程就是 waitNode 的属性
        else if (q == null)
            q = new WaitNode();
        // 默认第一次都会执行这里,执行成功之后,queued 就为 true,就不会再执行了
        // 把当前 waitNode 当做 waiters 链表的第一个
        else if (!queued)
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                 q.next = waiters, q);
        // 如果设置了超时时间,并过了超时时间的话,从 waiters 链表中删除当前 wait
        else if (timed) {
    
    
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {
    
    
                removeWaiter(q);
                return state;
            }
            // 没有过超时时间,线程进入 TIMED_WAITING 状态
            LockSupport.parkNanos(this, nanos);
        }
        // 没有设置超时时间,进入 WAITING 状态
        else
            LockSupport.park(this);
    }
}

cancel

Cancel the task, if it is running, try to interrupt

public boolean cancel(boolean mayInterruptIfRunning) {
    
    
    if (!(state == NEW &&//任务状态不是创建 并且不能把 new 状态置为取消,直接返回 false
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
    // 进行取消操作,打断可能会抛出异常,选择 try finally 的结构
    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.3 Implementation of Runnable interface method

run

The run method can be called directly, or a new thread can be opened for calling

  • The run method has no return value. By assigning a value to the outcome property (set(result)), the return value can be obtained from the outcome property when get;
  • The two constructors of FutureTask are finally converted into Callable, so when the run method is executed, only the call method of Callable needs to be executed. When the c.call() code is executed, if the input parameter is Runnable, the call path It is c.call() -> RunnableAdapter.call() -> Runnable.run(), if the input parameter is Callable, call it directly.
public void run() {
    
    
    // 状态不是任务创建,或者当前任务已经有线程在执行了,直接返回
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
    
    
        Callable<V> c = callable;
        // 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);
            }
            // 给 outcome 赋值
            if (ran)
                set(result);
        }
    } finally {
    
    
        runner = null;
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

3.4 runnale into callable

  • Constructor passes in Runnable and return value, and assigns to callable after conversion
public FutureTask(Runnable runnable, V result){
    
    
    // 将转化的Callable赋值给callable
    this.callable = Executors.callable(runnable, result);
}
  • Adapter mode: Executors.callable() --> RunnableAdpter
// 转化 Runnable 成 Callable 的工具类
// 自己实现Callable接口
static final class RunnableAdapter<T> implements Callable<T> {
    
    
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
    
    
        this.task = task;
        this.result = result;
    }
    // 重写call方法,返回result
    public T call() {
    
    
        task.run();
        return result;
    }
}

Finally, sum up what FutureTask does:

  1. Realize all the methods of Future, and have certain management functions for tasks, such as getting task execution results, canceling tasks, interrupting tasks and so on.
  2. Combines Callable, implements Runnable, and connects Callable and Runnnable in series.
  3. The two definition methods of tasks with and without parameters are unified, which is convenient to use.

Guess you like

Origin blog.csdn.net/weixin_43935927/article/details/108615345
Recommended