Future、ExecutorService源碼解析
Runnable創建的是沒有返回值的線程,同時還可以創建有返回值的線程。
1.整體架構
線程API之間的關係如下圖,
通過下面的代碼對各個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接口的類註釋,
- Future定義了異步計算。接口的方法用於檢查計算是否完成,等待計算完成,並取回計算結果等方法
- 使用 get方法可以得到結果,該方法會一直阻塞到子線程任務完成才返回
- 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;
}
}
- RunnableAdapter實現了Callable接口,所以該類本身就是任務類
- 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。