Future接口
官方解釋
先讓我們看一下谁不,官方對它的解釋:
A Future represents the result of an asynchronous computation. Methods are provided to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation. The result can only be retrieved using method get when the computation has completed, blocking if necessary until it is ready. Cancellation is performed by the cancel method. Additional methods are provided to determine if the task completed normally or was cancelled. Once a computation has completed, the computation cannot be cancelled. If you would like to use a Future for the sake of cancellability but not provide a usable result, you can declare types of the form Future<?> and return null as a result of the underlying task.
要點:
- 一個Future代表了一個異步計算的結(jié)果耍共。 它提供了可以堅持計算是否完成喷鸽、等待計算完成魂仍、檢索計算結(jié)果的方法酗捌。
- get方法獲得計算結(jié)果的唯一方法鹏控,如果計算沒有完成厦凤,此方法會堵塞直到計算完成。
- cancel方法可以用來取消這次計算车吹。一個已完成的計算是不能被取消的筹裕。
- isDone和isCancelled方法可以查詢計算是否正常完成還是被取消掉了。
- 如果我們不想知道此異步計算的結(jié)果窄驹,只是想隨時取消這次計算朝卒,可以通過聲明Future<?>并將get的返回值設為null。
源碼分析
public interface Future<V> {
//取消方法
boolean cancel(boolean mayInterruptIfRunning);
//計算是否被取消:如果計算在正常結(jié)束前被取消了乐埠,則返回true
boolean isCancelled();
//計算是否完成:不管是正常完成扎运、異常結(jié)束、還是被取消了饮戳,都返回true
boolean isDone();
//檢索返回結(jié)果,如果計算未完成洞拨,則等待任務完成扯罐。
V get() throws InterruptedException, ExecutionException;
//和get()方法類似,我們可以通過參數(shù)timout和unit指定等待的時間上限烦衣,如果時間結(jié)束了歹河,計算還未完成掩浙,就會拋出TimeOutException異常
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
V是實際計算結(jié)果的類型,也就是get()方法返回的類型秸歧。在其中厨姚,一共五個方法。
兩個狀態(tài)查詢方法(isCancelled和isDone方法), 一個取消計算方法(cancel)键菱,兩個檢索結(jié)果方法(get()和get(long,TimeUnit))方法谬墙。
對于已經(jīng)結(jié)束任務、已經(jīng)取消過的任務经备、不能被取消的任務拭抬,調(diào)用cancel會失敗并返回false; 如果任務還未開始,調(diào)用cancel后侵蒙,任務將不會在被執(zhí)行造虎,并返回true; 如果任務正在進行中,參數(shù)mayInterruptIfRunning為true,則中斷執(zhí)行此任務的線程纷闺,false算凿,任務則繼續(xù)執(zhí)行,直到完畢犁功。
總結(jié):到這兒氓轰,F(xiàn)uture之所以被設計的原因已經(jīng)很明了了,其實就是幫助我們可以自由的控制異步任務:可以通過它來查詢異步任務的執(zhí)行狀態(tài)波桩,取消任務戒努,也可以獲得正常的結(jié)果。
下面我們來看看FutureTask镐躲,F(xiàn)uture接口的實際實現(xiàn)類储玫。
Future的實現(xiàn):FutureTask
其實FutureTask并非直接實現(xiàn)自Future接口,而是RunnableFuture接口萤皂,RunnableFutrue是什么撒穷,看下源碼,便一目了然裆熙。
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
所以端礼,F(xiàn)utureTask同時實現(xiàn)了Runnable和Future兩大知名接口,所以FutureTask可以提交給Executor入录。
FutureTask源碼分析
首先我們來看一下FutureTask的兩個個構(gòu)造函數(shù)
//構(gòu)造函數(shù)一
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
//構(gòu)造函數(shù)二
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
源碼顯示蛤奥,不管我們使用哪個構(gòu)造函數(shù),其內(nèi)部都是把將傳入的參數(shù)保存為callable僚稿,并且把狀態(tài)置為NEW凡桥。FutureTask一共聲明了7個狀態(tài)。
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;
狀態(tài) | 說明 |
---|---|
NEW | 初始狀態(tài) |
COMPLETING | 進行中狀態(tài)蚀同,表示正在設置結(jié)果缅刽。很短暫的一個狀態(tài) |
NORMAL | 正常結(jié)束的狀態(tài) |
EXCEPTIONAL | 異常狀態(tài)啊掏,任務異常結(jié)束 |
CANCELLED | 任務成功被取消的狀態(tài) |
INTERRUPTING | 很短暫的狀態(tài),當在NEW狀態(tài)下衰猛,調(diào)用了cancel(true),則狀態(tài)就會轉(zhuǎn)換為INTERRUPTING迟蜜,直到執(zhí)行了Thread#interrupt()方法,狀態(tài)轉(zhuǎn)換為INTERRUPTED |
INTERRUPTED | 任務被中斷后的狀態(tài) |
run()方法講解
FatureTask被創(chuàng)建后啡省,下面進入run()方法娜睛,
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 = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
在run()方法中,先進行狀態(tài)檢查冕杠,檢查是否處于NEW狀態(tài)微姊,然后將執(zhí)行線程的引用保存在runner的變量。FurtureTask的runner變量用來引用任務執(zhí)行所在的線程分预。然后執(zhí)行Callable的call的方法兢交,進行任務執(zhí)行。接下來會出現(xiàn)兩種情況:
情況一: 如果執(zhí)行順利完成笼痹,則調(diào)用set(result)的方法配喳。在set()方法中,先將狀態(tài)置為COMPLETING凳干,然后將執(zhí)行結(jié)果保存到全局變量outcome中晴裹,然后將狀態(tài)置為NORMAL。然后調(diào)動finishCompletion()方法救赐,通知所有等待結(jié)果的線程涧团,并調(diào)用done()(在這里是個空方法)
情況二:如果執(zhí)行出現(xiàn)了異常。則執(zhí)行setException()方法经磅。在setException()方法中泌绣,操作基本和set()方法一樣,只是outcome保存的是Throwable预厌。
全局outcome變量:
private Object outcome;
流程圖
st=>start: Start
cond1=>condition: 處于NEW&&runner賦值成功
op1=>operation: 執(zhí)行任務
cond2=>condition: 發(fā)生異常?
op2=>operation: 設置結(jié)果
op3=>operation: 設置異常結(jié)果
op4=>operation: runner設置為null
e=>end
op=>operation: My Operation
st->cond1
cond1(yes)->op1->cond2
cond1(no)->e
cond2(yes)->op2->op4->e
cond2(no)->op3->op4->e
run()方法的大致操作流程就是這樣的阿迈。
FutureTask對Future接口界面的實現(xiàn)
isCanceled()和isDone()
public boolean isCancelled() {
return state >= CANCELLED;
}
public boolean isDone() {
return state != NEW;
}
當前任務的狀態(tài)保存在全局變量state中。這里檢查是否取消和是否完成轧叽,只要檢查一下state的值即可苗沧。
cancel(boolean)方法
下面,我們看一下cancel方法的實現(xiàn)
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;
}
cancel方面會直接檢測當前狀態(tài)是否是NEW,如果不是炭晒,說明任務已經(jīng)完成或取消或中斷待逞,所以直接返回。當符合條件后网严,檢查mayInterruptIfRunning的值识樱,
- 如果mayInterruptIfRunning == false,則直接將狀態(tài)設置為CANCELLED,并且調(diào)用finishCompletion()方法,通知正在等待結(jié)果的線程牺荠。
- 如果mayInterruptIfRunning == true,則暫時將狀態(tài)設置為INTERRUPTING,然后試著中斷線程驴一,完成后將狀態(tài)設置為INTERRUPTED休雌,最后調(diào)用finishCompletion()方法,通知正在等待結(jié)果的線程肝断。
get()方法的實現(xiàn)
get()方法首先還是進行狀態(tài)監(jiān)測杈曲,如果現(xiàn)在正處于NEW和COMPLETING狀態(tài),則會調(diào)用awaitDone()胸懈,直到狀態(tài)的轉(zhuǎn)變?yōu)槠渌麪顟B(tài)担扑,然后調(diào)用report()方法,在report()方法中趣钱,首先監(jiān)測狀態(tài):如果是NORMAL狀態(tài)涌献,直接返回保存在outCome中的結(jié)果;如果是CANCELLED首有、INTERRUPTING燕垃、INTERRUPTED狀態(tài),則拋出CancellationException()井联;如果處于其他狀態(tài)則拋出ExecutionException(比如調(diào)用了get(true,atime)方法卜壕,時間到期后狀態(tài)可能還處于NEW狀態(tài))。
源碼
/**
* @throws CancellationException {@inheritDoc}
*/
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
/**
* Returns result or throws exception for completed task.
*
* @param s completed state value
*/
@SuppressWarnings("unchecked")
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);
}
其中awaitDone(boolean timed, long nanos)方法用于等待任務完成烙常、任務中斷或時間到期轴捎。在此方法體中,顯示將這次等待保存到全局變量watiers中蚕脏,用于記錄所有調(diào)用了get()渴望獲得結(jié)果并堵塞的Thread侦副,然后不停的循環(huán)查詢state。直到時間到期或執(zhí)行完成蝗锥,則將循環(huán)中斷跃洛,返回轉(zhuǎn)變后的state供report()方法使用。
注:在FutureTask的的源碼中终议,使用了sun.misc.Unsafe進行狀態(tài)的賦值等操作汇竭,這是一個強大的對內(nèi)存進行操作的類,可以通過它繞過jdk的很多限制穴张。