Android concurrent(二)—— Future和FutureTask

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.

要點:

  1. 一個Future代表了一個異步計算的結(jié)果耍共。 它提供了可以堅持計算是否完成喷鸽、等待計算完成魂仍、檢索計算結(jié)果的方法酗捌。
  2. get方法獲得計算結(jié)果的唯一方法鹏控,如果計算沒有完成厦凤,此方法會堵塞直到計算完成。
  3. cancel方法可以用來取消這次計算车吹。一個已完成的計算是不能被取消的筹裕。
  4. isDone和isCancelled方法可以查詢計算是否正常完成還是被取消掉了。
  5. 如果我們不想知道此異步計算的結(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的值识樱,

  1. 如果mayInterruptIfRunning == false,則直接將狀態(tài)設置為CANCELLED,并且調(diào)用finishCompletion()方法,通知正在等待結(jié)果的線程牺荠。
  2. 如果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的很多限制穴张。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末细燎,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子皂甘,更是在濱河造成了極大的恐慌玻驻,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異璧瞬,居然都是意外死亡户辫,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門嗤锉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來渔欢,“玉大人,你說我怎么就攤上這事瘟忱“露睿” “怎么了?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵访诱,是天一觀的道長垫挨。 經(jīng)常有香客問我,道長触菜,這世上最難降的妖魔是什么九榔? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮玫氢,結(jié)果婚禮上帚屉,老公的妹妹穿的比我還像新娘。我一直安慰自己漾峡,他們只是感情好攻旦,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著生逸,像睡著了一般牢屋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上槽袄,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天烙无,我揣著相機與錄音,去河邊找鬼遍尺。 笑死截酷,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的乾戏。 我是一名探鬼主播迂苛,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼鼓择!你這毒婦竟也來了三幻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤呐能,失蹤者是張志新(化名)和其女友劉穎念搬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡朗徊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年首妖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片爷恳。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡悯搔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出舌仍,到底是詐尸還是另有隱情,我是刑警寧澤通危,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布铸豁,位于F島的核電站,受9級特大地震影響菊碟,放射性物質(zhì)發(fā)生泄漏节芥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一逆害、第九天 我趴在偏房一處隱蔽的房頂上張望头镊。 院中可真熱鬧,春花似錦魄幕、人聲如沸相艇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽坛芽。三九已至,卻和暖如春翼抠,著一層夾襖步出監(jiān)牢的瞬間咙轩,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工阴颖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留活喊,地道東北人。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓量愧,卻偏偏與公主長得像钾菊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子侠畔,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容