java常用類-- Future、FutureTask源碼解析

一、整體說明

通常情況下,我們通過 extendsThread 或者implements Runnable接口來創建線程,線程沒有返回值;

那如何創建有返回值的線程呢?

先來看一個簡單的demo:

// 首先我們創建了一個線程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(3, 3, 0L, TimeUnit.MILLISECONDS,
                                                     new LinkedBlockingQueue<>());
// futureTask 我們叫做線程任務,構造器的入參是 Callable
FutureTask futureTask = new FutureTask(new Callable<String> () {
  @Override
  public String call() throws Exception {
    Thread.sleep(3000);
    // 返回一句話
    return "我是子線程"+Thread.currentThread().getName();
  }
});
// 把任務提交到線程池中,線程池會分配線程幫我們執行任務
executor.submit(futureTask);
// 得到任務執行的結果
String result = (String) futureTask.get();
log.info("result is {}",result);

在正式學習如何創建有返回值的線程之前,先來看一下關於線程 API 之間關係的依賴圖:

圖片描述

 二、各種 API 的底層實現

1 Callable

Callable 是一個接口,約定了線程要做的事情,和 Runnable 一樣,不過這個線程任務是有返回值的,我們來看下接口定義:

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

我們使用的時候,都不會直接使用 Callable,而是會結合 FutureTask 一起使用;

FutureTask 是線程運行的具體任務,從上圖可以看到 FutureTask 實現了 RunnableFuture 接口,而 RunnableFuture 又實現了 Runnable, Future 兩個接口;

2 RunnableFuture

RunnableFuture也是一個接口,最大目的,是讓 Future 對 Runnable 進行管理,可以取消 Runnable,查看 Runnable 是否完成等等;接口定義如下:

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

3 Future

Future 定義了各種方法對任務進行了管理,比如說取消任務,得到任務的計算結果等等;

4 FutureTask

現在清楚了,新建線程任務有兩種方式,一種是無返回值的 Runnable,一種是有返回值的 Callable,FutureTask 實現了 RunnableFuture 接口,又集合了 Callable(Callable 是 FutureTask 的屬性),還提供了兩者一系列的轉化方法,這樣 FutureTask 就統一了 Callable 和 Runnable,我們一起看下:

public class FutureTask<V> implements RunnableFuture<V> {}

從源碼中可以看到,FutureTask 有如下重要的屬性:

// 任務狀態
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;

從源碼中可以看到,FutureTask 有兩個構造器,分別接受 Callable 和 Runnable,如下:

// 使用 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
}

我們注意到入參是 Runnable 的構造器,會使用 Executors.callable 方法來把 Runnnable 轉化成 Callable,Runnnable 和 Callable 兩者都是接口,兩者之間是無法進行轉化的,所以 Java 新建了一個轉化類:RunnableAdapter 來進行轉化,我們來看下轉化的邏輯:

public static <T> Callable<T> callable(Runnable task, T result) {
        if (task == null)
            throw new NullPointerException();
        return new RunnableAdapter<T>(task, result);
    }
// 轉化 Runnable 成 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;
    }
    public T call() {
        task.run();
        return result;
    }
}

這是一個典型的適配模型,我們要把 Runnable 適配成 Callable,首先要實現 Callable 的接口,接着在 Callable 的 call 方法裏面調用被適配對象(Runnable)的方法。

另外,來看下幾個關鍵方法的實現源碼:

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);
    }
}
/**
 * run 方法可以直接被調用
 * 也可以開啓新的線程進行調用
 */
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);
    }
}
// 取消任務,如果正在運行,嘗試去打斷
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;
}

FutureTask 是關鍵,通過 FutureTask 把 Runnnable、Callable、Future 都串起來了,使 FutureTask 具有三者的功能,統一了 Runnnable 和 Callable,更方便使用

發佈了95 篇原創文章 · 獲贊 15 · 訪問量 5242
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章