線程:Future、ExecutorService源碼解析


Runnable創建的是沒有返回值的線程,同時還可以創建有返回值的線程。

1.整體架構

線程API之間的關係如下圖,
image
通過下面的代碼對各個API的使用做演示,

// 創建一個線程池(實際開發時線程池不是這樣創建的,這裏只是演示)
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);
  • Callable:定義需要執行的任務,可以有返回值
  • FutureTask:線程任務,入參是Callable,是對Callable的包裝,方便線程池的使用。通過FutureTask.get方法獲取子線程計算結果

2.Callable接口

Callable接口定義了線程執行任務的代碼,與Runnable的作用相同,區別是Callable有返回值。接口的定義如下,

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

返回值是泛型,使用時不會直接使用Callable,而是會結合FutureTask一起使用。

3.FutureTask類

FutureTask是線程的具體執行,該類實現了RunnableFuture接口。RunnableFuture接口是Runnable和Future接口的子接口。通過這一個繼承關係逐一進行說明。

Future接口

Callable接口可以返回子線程執行結果,而Future接口用於獲取Callable接口實現類的返回結果

Future接口的類註釋,

  1. Future定義了異步計算。接口的方法用於檢查計算是否完成,等待計算完成,並取回計算結果等方法
  2. 使用 get方法可以得到結果,該方法會一直阻塞到子線程任務完成才返回
  3. cancel 方法可以取消任務
// 如果任務執行成功或已被取消,則無法再取消的,直接返回true
// 如果任務還沒被進行時,發起取消,可以取消成功的。
// 如果取消時,任務已經在運行了,mayInterruptIfRunning 爲 true 的話,就可以打斷運行中的線程;反之,表示不能打斷直接返回
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;

Future接口定義了各種對任務進行管理的方法。

RunnableFuture接口

RunnableFuture也是一個接口,

public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

RunnableFuture接口的目的是將Runnable和Future進行整合,讓Future對Runnable進行管理

統一Callable和Runnable

之前對任務的創建方式進行了拓展,

  • 無返回值的 Runnable
  • 有返回值的 Callable

兩種接口的定義使得任務的創建方式並不統一,所以在 FutureTask類實現了RunnableFuture接口,同時集合了Callable接口(因爲 Callable是FutureTask的屬性),還提供了兩者的轉化方法

1)FutureTask類定義

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

從類定義上可以看出來 FutureTask 實現了 RunnableFuture 接口,即間接實現了 Runnnable 接口,故FutureTask 本身就是個 Runnnable。同時 FutureTask 也實現了 Future,也就是說 FutureTask 具備對任務進行管理的功能。

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

Callable是FutureTask的屬性,這使得 FutureTask具備了將Runnable轉化爲Callable的前提。

3)FutureTask構造器

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
}

FutureTask的兩個構造器最終的目的是將入參都轉化爲Callable。

4)Runnable適配Callable

當FutureTask的構造方法傳入的參數爲Runnable類型,會使用Executors類的callable方法將 Runnable轉化爲 Callable。因爲Runnable和Callable都是接口,無法直接轉化,所以此處引入一個RunnableAdapter類進行轉化。該類是Executors類的靜態內部類,相當於一個適配器,將Runnable適配爲Callable。

Executors類是一個工廠類,callable方法創建一個 RunnableAdapter對象並返回,

public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}

RunnableAdapter類中對 Runnable的適配方式很簡單,

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;
    }
}
  1. RunnableAdapter實現了Callable接口,所以該類本身就是任務類
  2. Runnable是RunnableAdapter的一個屬性,在RunnableAdapter複寫的 Callable的call方法中,調用了Runnable的run方法。

FutureTask對任務的管理

Future接口對任務的管理定義了一系列方法,FutureTask是Future接口的子類,所以FutureTask對任務管理方法進行了複寫。幾個較爲關鍵的方法進行說明,

1)get方法

get方法有無限阻塞和設置超時時間兩種,一般使用設置超時時間的方法,

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

對幾處代碼進行說明,

 // 任務正在被執行,且等待一定的時間後,仍然在執行中,直接拋出異常
(s <= COMPLETING && (s = awaitDone(true, unit.toNanos(timeout))) <= COMPLETING)

awaitDone方法源碼如下,

/**
 * 返回線程任務執行結果,完成、中斷或超時
 * @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) {		  // 任務已經執行結束(完成、取消或打斷),返回任務狀態s
            if (q != null)			  // q是WaitNode對象(FutureTask的內部類),用於記錄等待線程
                q.thread = null;	  // WaitNode的thread屬性指向當前線程,線程執行完成後將該線程置空
            return s;
        }
        else if (s == COMPLETING)	// 如果任務正在執行且未超時,當前線程讓出cpu使用權,重新競爭
            Thread.yield();
        else if (q == null)
            q = new WaitNode();		// 第一次運行時新建 WaitNode,當前線程就是WaitNode對象的thread屬性
            
        else if (!queued)			// 第一次都會執行這裏,執行成功之後,queued爲true,不會再執行。新建的WaitNode對象當做waiters鏈表的第一個元素
            queued = UNSAFE.compareAndSwapObject(this, waitersOffset,	
                                                 q.next = waiters, q);
       
        // 任務既沒有被運行也沒有結束,對應NEW狀態                                           
        else if (timed) {			// 如果設置超時
            nanos = deadline - System.nanoTime();
            if (nanos <= 0L) {		// 已經超時,從鏈表中刪除當前WaitNode節點
                removeWaiter(q);
                return state;
            }
            LockSupport.parkNanos(this, nanos);	// 還未超時則進入TIMED_WAITING狀態
        }
        else					 // 未設置超時,進入WAITING狀態
            LockSupport.park(this);
    }
}

get 方法中做了很多 wait 的事情,當發現任務還在進行中,沒有完成時,就會阻塞當前進程,等待任務完成後再返回結果值。阻塞底層使用的是 LockSupport.park 方法,使當前線程進入 WAITING 或 TIMED_WAITING 狀態。

2)run方法

public void run() {
    // 任務不是新建的或者當前任務正在被執行,直接返回
    if (state != NEW ||
        !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                     null, Thread.currentThread()))
        return;
    try {
        Callable<V> c = callable;
        // Callable不爲空,該任務創建成功且未被執行,狀態爲NEW
        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 = null;
        int s = state;
        if (s >= INTERRUPTING)
            handlePossibleCancellationInterrupt(s);
    }
}

在執行 c.call() 代碼時,

  • 如果入參是 Runnable 的話, 調用路徑爲 c.call() -> RunnableAdapter.call() -> Runnable.run()
  • 如果入參是 Callable 的話,直接調用Callable的call方法

3)cancel方法

public boolean cancel(boolean mayInterruptIfRunning) {
    if (!(state == NEW &&	//任務狀態不是創建 並且不能把 new 狀態置爲取消,直接返回 false
          UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
              mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
        return false;
        
    // 進行取消操作,打斷可能會拋出異常
    try { 
        if (mayInterruptIfRunning) {	// mayInterruptIfRunning 爲 true 的話,就可以打斷運行中的線程
            try {
                Thread t = runner;
                if (t != null)
                    t.interrupt();
            } finally {
                //狀態設置成已打斷
                UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
            }
        }
    } finally {
        // 清理線程
        finishCompletion();
    }
    return true;
}

總結

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章