有返回結果的異步任務Futrue與Callable

2021-01-10 騰訊網

上一篇講解了線程池execute方法詳細執行過程,execute接受Runnable參數但沒有返回執行結果,在需要有執行結果的地方就不太適用了。

異步任務有返回值

如果出現一個計算量稍大用時較長而後面需要結果的情況,可以先把這個任務提交到另外的線程異步執行,主線程仍然繼續做運行等到需要計算結果的時候再來獲取,但是提供異步計算Runnable或者說線程Thread都不返回結果。

但Java中提供了Future與Callable來實現可以獲取異步執行結果的一套框架,在上一篇文章中線程池的execute方法沒有返回值,但是線程池提供的submit方法支持返回值,而submit利用Future與Callable實現

Future與Callable簡單解釋

Callable接口聲明了一個方法call提供一個泛型的返回結果,

Future接口聲明了5個接口:

cancel方法:取消任務,如果任務取消成功則返回true,如果任務取消失敗則返回false。參數mayInterruptIfRunning表示是否允許取消正在執行卻沒有執行完畢的任務,如果設置true,則表示可以取消正在執行過程中的任務。如果任務已經完成,則都返回false。;。

isCancelled方法:表示任務是否已經被取消。

isDone方法:表示任務是否已經完成,若任務完成,則返回true;

get()方法:用來獲取任務結果,這個方法會阻塞線程,會一直等到任務執行完畢才返回;get(long timeout, TimeUnit unit)方法如果在指定時間內,還沒獲取到結果,就直接返回null。

再看線程池的submit

線程池提供了三個submit都繼承至AbstractExecutorService的實現,源碼如下圖:

三個方法接受不同的參數,但都會作為newTaskFor方法的參數生成一個RunnableFuture類的對象,所以最關鍵在於RunnableFuture類。

而RunnableFuture也簡單是一個接口並且繼承了Runnable與Future,所以它可以被當作Runnable用在線程池的execute方法中,而同時又擁有Future的所有功能。

而submit通過newTaskFor生成的實際上是RunnableFuture子類,通過看源碼實際上生成的是RunnableFuture的實現類FutureTask,最終的關鍵就是FutureTask類。

最關鍵類FutureTask詳解

FutureTask實現RunnableFuture,RunnableFuture繼承Runnable與Future,所以可以當作Runnable在線程池中使用,作為execute方法的參數,同時它也是Future的真正實現者,它的關鍵屬性如下圖:

前面提到的Future幾個方法在FutureTask中得到了實現,實現也很簡單,源碼如上圖FutureTask維護一個狀態state,來表示任務狀態取消、中斷、完成等。

我們都是通過Future的get方法來獲取結果,而FutureTask對get方法實現也簡單:

首先判斷state的狀態,如果大於3則拋出異常;

等於3則說明已經完成會執行Thread.yield()讓出CPU;

小於3則說明任務還沒有完成,就會把線程組裝成WaitNode加入到waiters中,並中斷當前線程

從上一篇知道execute最終是執行參數的run方法,在這裡也就是FutureTask的run方法執行過程如下:

首先執行callable的call方法獲取到返回結果;

CAS方式把狀態從0設置為1;

如果設置成功(保證線程安全)把結果賦值給outcome;

第四步把狀態從1設置為2,表示完成;

最後喚醒waiters鍊表中的線程

總結

可以看到有返回結果的異步實現最終依賴FutureTask,它同時實現了Runnable與Future,擁有一個Callable屬性。get方法根據FutureTask的狀態會把線程掛起並放到等待鍊表中了。

同時它可以被用到線程池中被執行,在線程池中最後會調用它的run方法,run方法會調用Callable的call方法也就是真正計算的方法,返回結果後會修改FutureTask的狀態並喚醒等待鍊表的線程

線程池的submit方法還支持Runnable參數,但是FutureTask執行的是Callable的call方法,那麼Runnable中的run是怎麼轉換成Callable的call方法呢?

實際上也很簡單,採用適配器模式,建一個Callable的子類RunnableAdapter,它保存一個Runnable屬性,RunnableAdapter的call方法調用Runnable的run方法,至於返回的結果為null或者自己傳一個結果。

所以要實現有返回結果的異步任務,要麼實現Callable並實現call方法,或者創建一個Runnable的實現類也行。

Java程式設計師日常學習筆記,如理解有誤歡迎各位交流討論!

相關焦點

  • 原理(二)、Callable和Future
    結果說明:將"線程池的拒絕策略"由DiscardPolicy修改為DiscardOldestPolicy之後,當有任務添加到線程池被拒絕時,線程池會丟棄阻塞隊列中末尾的任務,然後將被拒絕的任務添加到末尾。
  • 關於異步任務的設計探索(2)
    編輯導語:在上一篇文章《關於異步任務的設計探索(1)》中,作者為我們解答了異步任務的設計邏輯是什麼?有哪些需要注意的問題?在本篇文章中,作者又繼續異步任務設計的故事,從代碼層面上分享了關於異步任務的設計探索。
  • JAVA中使用CompletableFuture進行異步編程
    CompletableFuture異步任務執行線程池,默認是把異步任務都放在ForkJoinPool中執行。在這種方式中,主線程不會被阻塞,不需要一直等到子線程完成。主線程可以並行的執行其他任務。thenAccept方法:消費處理結果。接收任務的處理結果,並消費處理,無返回結果。thenRun方法:只要上面的任務執行完成,就開始執行thenRun,只是處理完任務後,執行 thenRun的後續操作帶有Async默認是異步執行的。
  • 《Java 8 in Action》CompletableFuture:組合式異步編程
    Future接口Future接口在Java5中被引入,設計初衷是對將來某個時刻會發生的結果進行建模。它建模了一種異步計算,返回一個執行運算結果的引用,當運算結束後,這個引用被返回給調用方。在Future中觸發那些潛在耗時的操作把調用線程解放出來,讓它能繼續執行其他有價值的工作,不再需要等待耗時的操作完成。
  • 通俗系列之同步、異步、阻塞和非阻塞
    那究竟什麼是同步、異步、阻塞和非阻塞呢?我決定嘗試用比較通俗的例子舉例來解釋一下,於是便有了這篇文章。同步概念什麼是同步?同步就是一個任務的完成依賴於另外一個任務,只有被依賴的任務完成後,那麼依賴的任務才能繼續完成。
  • Django 3.1異步視圖實例學習
    在關注其代碼時候,發現其示例都是異步的寫法。可以肯定雖然這不是Django,但是肯定是類似的框架。綜合考慮,Django將目前默認同步執行的視圖改為異步還是非常有意義的。雖然等待I/O操作數微秒時,但是這會阻塞。如果換成異步就不會任何阻塞,可以同時處理其他任務,從而以較低的延遲處理更多的請求。這尤其對Facebook這樣的大型網站性能改善而言。
  • NIO、BIO、AIO、同步異步、阻塞非阻塞傻傻分不清楚?
    阻塞與非阻塞阻塞和非阻塞操作是針對發起的IO請求操作後是否立刻返回一個標誌信息而不讓請求線程等待,當數據準備未完成時,請求線程的狀態:阻塞:往往需要等待緩衝區中的數據準備好過後才處理其他的事情,否則一直等待在那裡。非阻塞:無論數據是否準備好,都會直接返回。
  • 基於C++模板,實現三種異步收發數據的方法
    std::future和std::promise兩者結合可以實現異步的功能場景,本文將介紹的異步收發數據模版類是在實踐中結合std::future和std::promise而摸索出來的。工作過程中,我們可能會經常遇到這樣的場景,需要從線程中獲取運行的結果。現在我們有兩種方式可以實現這樣的效果。
  • Java 實現線程的方式有幾種方式?帶有返回值的線程怎麼實現?
    Java 實現線程的方式有幾種方式?帶有返回值的線程怎麼實現?在Java線程開發中,有幾種方法開啟線程?假如需要得到線程返回的信息怎麼辦?可以實現嗎?凱哥將通過源碼和大家一起分享下線程怎麼將返回值帶回來的。
  • 採用BC定時查詢方式的總線控制器異步通訊處理方案
    總線控制器是在數據總線上被指定執行啟動信息傳輸任務的終端。1553B總線上消息傳輸的過程是:總線控制器向某一終端發布一個接收/發送指令,終端在給定的響應時間範圍內發回一個狀態字並執行消息的接收/發送。 在1553B數據總線上,消息是按時間逐次進行傳輸的,總線上BC與RT間有10種可能的通信模式。大多消息的處理按固定的順序、周期和相位出現。
  • 三相異步電動機維護保養_三相異步電動機額定電流計算
    三相異步電動機維護保養_三相異步電動機額定電流計算 網絡整理 發表於 2020-03-16 09:03:38   三相異步電動機維護保養   我國中小型異步裝機容量已達
  • 三相異步電機的調整方法
    三相異步電機的調整方法 三相異步電動機轉速公式為:n=60f/p(1-s) 從上式可見,改變供電頻率f、電動機的極對數p及轉差率s均可太到改變轉速的目的。以後是三相異步電動機的七種調速方式及其特點,指明其適用的場合、情況。
  • 《魔獸世界》懷舊服返回詛咒之地任務怎麼做 返回詛咒之地任務完成...
    導 讀 魔獸世界懷舊服已經有一段時間了對某些任務一定有些疑問,相信大家一直在尋找中返回詛咒之地任務的任務攻略
  • 多線程有哪些優點?-Python每日3題(多線程專題)
    [Normal] Python異步使用場景有哪些? 思考30秒再往下翻... 由於系統切換線程的開銷也很大,所以,一旦線程數量過多,CPU的時間就花在線程切換上了,真正運行代碼的時間就少了,結果導致性能嚴重下降。 由於我們要解決的問題是CPU高速執行能力和IO設備的龜速嚴重不匹配,多線程和多進程只是解決這一問題的一種方法。 另一種解決IO問題的方法是異步。
  • RMxprt在三相異步電動機中的2D電磁場分析
    針對如何才能更好地計算仿真三相異步電動機,求解二維和三維有限元件這一問題。文中以一臺三相異步電動機為藍本,分析RMxprt模塊在三相異步電動機的2D電磁場的應用。將實驗計算結果與有限元結果進行橫向對比,誤差僅0.366 2%,符合工程需要。
  • JavaScript-window對象常用屬性及方法有哪些?
    a)點擊取消,返回值為nullb)沒有默認值如果用戶沒有輸入內容,返回一個空字符串如果用戶輸入了內容,返回值為用戶輸入的內容c)有默認值如果用戶沒有輸入內容,返回默認值;},2000);總結說明:要把定時器下邊的任務執行完畢後 才會去執行定時器的內容定時執行中this指向window每創建一個定時器 就會有一個唯一的id
  • 單相異步電動機型號及分類
    機座代號——以電機軸心高(mm)表示,規格有:45、50、56、63、71、80、90、100。 特徵代號——表示電機的鐵芯長度和極數,鐵芯長度號有l(長)、m(中)、s(短)及數字1、2。特徵代號的後面一位為極數,2、4、6等偶數。 特殊環境代號——表示產品適用的環境,一般環境不標註,見表2。
  • 三相異步電動機是怎樣轉動起來的
    1、帶傳動有何優缺點?答:優點:傳動平穩,無噪聲;有過載保護作用;傳動距離較大;結構簡單,維護方便,成本低。缺點:傳動比不能保證;結構不夠緊湊;使用壽命短;傳動效率低;不適用於高溫、易燃、易爆場合。2、三相異步電動機是怎樣轉動起來的?