揭開Future的神秘面紗 結果獲取

2022-04-07 01:59:41 字數 3918 閱讀 9374

在前面的兩篇博文中,已經介紹利用futuretask任務的執行流程,以及利用其實現的cancel方法取消任務的情況。本篇就來介紹下,執行緒任務的結果獲取。

我們知道利用future介面的最重要的操作就是要獲取任務的結果,而此操作對應的方法就是get。但是問題來了,如果我呼叫get方法的時候,任務還沒有完成呢?答案就是,等它完成,當前執行緒將被阻塞,直到任務完成(注意,這裡說的完成,指的是任務結束,因為異常而結束也算),get方法返回。主線程(不是執行任務的執行緒)才被喚醒,然後繼續執行。

有人可能會問,如果我呼叫get方法的時候,任務離完成還需要很長時間,那麼我主線程不是會浪費一些時間?是的,如果主線程比較忙的話,這樣確實主線程的效率。不過還有乙個有參的get方法,此方法以等待時長為引數,如果時長結束,任務還沒完成,主線程將繼續執行,然後會在之後的某個時間再來獲取任務結果。(當然如果主線程依賴這個任務結果才能繼續執行,那麼只能老老實實地等了

waitnode類**如下:

static final class

waitnode

}

waitnode保留執行緒引用的作用是什麼?答案是用於任務完成後喚醒等待執行緒。當futuretask執行完callable的run方法後,將執行finishcompletion方法通知所有等待執行緒

private

void

finishcompletion()

//獲取下乙個節點

waitnode next =q.next;

if (next == null

)

break

; q.next = null; //

unlink to help gc

q =next;

}break

; }

}//呼叫鉤子函式done,此為空方法,子類可根據需求進行實現

done();

callable = null

;

}

執行緒的阻塞方式——park和unparkpark/unpark也是用於控制線程等待狀態的。我們熟悉的,用於控制線程等待狀態的還有wait/notify。wait/notify是某個物件的條件佇列,要阻塞執行緒,或者說要加入等待佇列,必須先獲取物件的鎖。

與wait()/notify不同的是,park和unpark直接操作執行緒,無需獲取物件的鎖,個人認為這是這裡使用park/unpark,而不是wait/notifyall的原因,因為獲取鎖需要額外的開銷。

以下是futuretask中get方法的實現

public v get

() throws interruptedexception, executionexception

以下是get方法中呼叫到的awaitdone的實現

private

int awaitdone(boolean timed, long

nanos) throws interruptedexception

int s =state;

//如果狀態大於completing,也就是任務已結束,返回任務狀態

if (s >completing)

else

if (s == completing) //

cannot time out yet

thread.yield

();

//第一次迴圈,q是null,建立節點

else

if (q == null

) q = new

waitnode();

//如果還未加入等待佇列,就加入。加入等待佇列的目的是,當任務完成的時候能夠通過控制代碼及時喚醒正在等待的執行緒。注意:加入佇列的時候,還沒有掛起。

else

if (!queued)

= waiters 表示式的返回值 是左側的值,也就是waiters

//意思是,如果當前物件的waiters的值是waiters, 就將他賦值為q

queued = unsafe.compareandswapobject(this

, waitersoffset,

q.next =waiters, q);

//如果是超時等待,則呼叫parknanos, 執行緒將在指定時間後被喚醒。目的是先掛起執行緒,時間到了再喚醒出來,此時還在for迴圈中,將再次執行符合條件的if塊

else

if(timed)

locksupport.parknanos(

this

, nanos);

}//如果不是超時等待,且已經加入等待佇列,這時候利用park將當前執行緒掛起

else

locksupport.park(

this

); }

}

很多人可能會覺得這個迴圈體,看著有點迷糊,我剛開始也看得頭大。但是我們可以根據幾種情境,來檢視這幾種情境下**的執**況。

注:第二個for迴圈內,第二個if-else塊是乙個大塊,每次只執行乙個。

幾種執**境

一、當前執行緒成功加入等待佇列,且被阻塞,一段時間後任務完成,執行緒被喚醒

二、當前執行緒加入佇列後,還沒被阻塞,任務就已經完成了

三、因為其他執行緒加入等待佇列的影響,當前執行緒未能加入等待佇列

這裡說明一下,如果其他執行緒在此執行緒之前,比較接近的時間,加入了等待佇列,由於記憶體可見性的原因,當前執行緒看到的waiters值沒有及時改變,故與其實際值不同,cas操作就將失敗。

為什麼一定要cas成功?答案是,如果不成功,出現執行緒安全問題,鍊錶的結構就會一塌糊塗。這裡不細談。

根據任務狀態獲取結果

我們已經知道,futuretask有乙個object欄位的outcome,也就是任務執行的結果。當任務完成後,會將結果賦值給它。以下是futuretask的run方法:

public

void

run()

catch

(throwable ex)

//如果執行完成,把結果賦值給outcome

if(ran)

set(result); //

這個方法會呼叫finishcompletion

} }

finally

}

由前文可知,當任務"完成"的時候,獲取結果的執行緒將被喚醒。回到get方法,它將獲取到任務的狀態,並根據任務狀態獲取結果。也就是report方法:

private v report(int

s) throws executionexception

揭開信貸神秘面紗

貸款信用保險是指保險人對銀行或其他金融機構與企業之間的借貸合同進行擔保,以承保借款人信譽風險的保險。在貸款信用保險中,貸款方 即債權人 是投保人。但保單簽發後,貸款方即成為被保險人。當企業無法歸還貸款時,債權人可以從保險那裡獲得補償。貸款人在獲得保險人的補償後,必須將債權轉讓給保險人,由保險人向借款...

揭開Zookeeper神秘面紗

zookeeper是乙個開源的分布式的,為分布式應用提供協調服務的apache專案。hadoop 和hbase的重要元件。它是乙個為分布式應用 提供一致性服務 的軟體,提供的功能包括 配置維護 網域名稱服務 分布式同步 組服務 等。1 zookeeper 乙個領導者 leader 多個跟隨者 fol...

揭開MVC的神秘面紗

最近參加的高校平台專案中涉及到了 mvc。mvc,有人說是一種設計模式,也有人說是使用者介面層設計架構。那麼 mvc到底是什麼呢?今天我們一起來學習一下。mvc,即model view controller,把乙個應用的輸入 處理 輸出流程按照 model view controller 的方式進行...