更好地理解與使用Future

一個多月沒有寫東西了,今天想寫的也是想記錄下來的一些學習及思考結果,記憶能力有限,避免時間長久就忘記了,今天想寫的也仍是一些基礎的東西,爲何我老是關注這些平時碼業務代碼不多能用到的又比較基礎東西呢,主要是由於我以爲可能光寫簡單的業務代碼可能不多有機會會遇到難題,可是也有萬一,萬一遇到了怎麼解決,萬變不離其宗,基礎知識的深度決定上層建築的高度,所以,扯正題吧,今天寫的是關於JDK裏面的Future。
多線程


Future模式

Future模式的核心思想是可以讓主線程將原來須要同步等待的這段時間用來作其餘的事情,這個時候能夠更好的利用CPU分片,這點能夠這麼理解,若是咱們申請了線程,可是又沒讓CPU調度,這豈不是很浪費,咱們能夠有更好的方法來提升CPU的利用率,也就是讓線程原本能夠等待(歇息)的時間讓線程去作其餘的事情,榨乾它的剩餘價值,固然是以CPU沒有打滿(100%利用率)爲前提,JAVA裏面對Future模式的具體實現是JDK1.5開始的JCU包中的Future接口及其實現的定義。工具

不一樣的工做方式學習

上圖簡單描述了不使用Future和使用Future的區別,不使用Future模式,主線程在invoke完一些耗時邏輯以後須要等待,這個耗時邏輯在實際應用中多是一次RPC調用,多是一個本地IO操做等。B圖表達的是使用Future模式以後,咱們主線程在invoke以後能夠當即返回,去作其餘的事情,回頭再來看看剛纔提交的invoke有沒有結果。spa


JAVA中Future模式定義的行爲

咱們來一塊兒看看JAVA中Future接口的定義:線程

Future接口定義代理

接口定義行爲,咱們經過上圖能夠看到實現Future接口的子類會具備哪些行爲,假設咱們已經委託了系統一些執行邏輯,那麼對於這個執行邏輯,咱們有:對象

  1. 咱們能夠取消這個執行邏輯,若是這個邏輯已經正在執行,提供可選的參數來控制是否取消已經正在執行的邏輯。接口

  2. 咱們能夠判斷執行邏輯是否已經被取消。生命週期

  3. 咱們能夠判斷執行邏輯是否已經執行完成。隊列

  4. 咱們能夠獲取執行邏輯的執行結果。

  5. 咱們能夠容許在必定時間內去等待獲取執行結果,若是超過這個時間,拋TimeoutException。


線程池中的FutureTask

在JCU中,FutureTask是Future的具體實現,額外實現了Runnable接口,既然實現Runnable接口,那麼就知足了Task的行爲,因而咱們獲得了一個能夠被用來執行的Future。值得一提的是FutureTask的身份,她是JCU提供的線程池實現用到的任務基本單元,經常使用線程池的同窗都知道,線程池接收兩種對象,一個是Runnable任務,一種是Callable任務,二者區別在於前者返回執行結果給外部,後者須要。按照默認線程池是實現ExecutorService接口的,按照ExecutorService接口定義的行爲,咱們能夠將Runnable或Callable任務提交到線程池讓其去被執行,而被提交的Runnable或Callable任務都會被包裝成FutureTask,丟到任務隊列,由線程池的工做線程去執行。


FutureTask任務狀態流轉

當咱們把一個FutureTask丟到線程池任務隊列以後,任務後續的生命週期是怎麼樣的呢?在FutureTask中定義了七種任務狀態,咱們能夠一塊兒看一下:

  1. NEW:當FutureTask被初始建立的時候的狀態。

  2. COMPLETING:當任務被執行完畢,FutureTask會將執行結果設置給FutureTask的outcome屬性,在設置以前會將FutureTask的狀態修改成COMPLETING。

  3. NORMAL:當任務被執行完畢,FutureTask會將執行結果設置給FutureTask的outcome屬性,在設置以後會將FutureTask的狀態修改成NORMAL。

  4. EXCEPTIONAL:當任務在被執行的過程當中拋了異常,FutureTask會將異常信息設置給FutureTask的outcome屬性,在設置以前會將FutureTask的狀態修改成COMPLETING,在設置以後會將FutureTask的狀態修改成EXCEPTIONAL。

  5. CANCELLED:當外部想要取消任務,而又不容許當任務正在執行的時候被取消的時候會將FutureTask的狀態修改成CANCELLED。

  6. INTERRUPTING:當外部想要取消任務,同時容許當任務正在執行的時候被取消的時候,會先將FutureTask的狀態設置爲INTERRUPTING,而後設置執行任務的線程的中斷標記位。

  7. INTERRUPTED:當外部想要取消任務,同時容許當任務正在執行的時候被取消的時候,會先將FutureTask的狀態設置爲INTERRUPTING,而後設置執行任務的線程的中斷標記位,最後將Future的狀態設置爲INTERRUPTED。

綜上,咱們也能夠總結下FutureTask的狀態流轉可能流程:

  1. NEW—>COMPLETING—>NORMAL(任務執行正常)

  2. NEW—>COMPLETING—>EXCEPTIONAL(任務執行異常)

  3. NEW—>CANCELLED(不容許執行中的取消)

  4. NEW—>INTERRUPTING—>INTERRUPTED(容許執行中的取消)


總結

JCU包提供了很好的工具讓咱們可以快速的開發基於線程池的多線程應用,那麼當咱們把線程提交到線程池以後,站在單獨任務的角度,咱們關心的核心問題一般是下面幾點:

  1. 任務超時時間,一方面咱們不可以無限期的佔用線程資源,另外一方面咱們不可以讓外部無限期的等待,所以timeout變得尤其重要。

  2. 主動取消任務,假如咱們以爲timeout不夠靈活,一般場景是當咱們在timeout以前已經知道FutureTask不須要再繼續爲咱們工做的時候,咱們能夠先判斷任務是否已經done(isDone),若是沒有done,咱們能夠主動的將任務取消掉,這個時候Future定義的cancel能夠派上用場。

  3. 任務異常信息,還記得咱們最初提交的Runnable和Callable麼,當任務拋出了異常咱們如何get到異常信息呢,FutureTask實際上是代理了Runnable和Callable的執行,捕獲異常並將異常信息交給outcome,所以經過FutureTask,咱們一樣能夠得到任務內部拋出的異常信息。


文章由博主原創,若有須要歡迎轉載,若有錯誤多多包涵。

相關文章
相關標籤/搜索