揭開Future的神祕面紗——任務執行

貓毛·波拿巴發表於2018-11-03

前言

  此文承接之前的博文 解開Future的神祕面紗之取消任務 補充一些任務執行的一些細節,並從全域性介紹程式的執行情況。

系列目錄

任務的提交與執行

任務提交到執行的流程

  

 

 

前文我們已經瞭解到一些Future的實現細節,這裡我們來梳理一下執行流程。注意:這裡說的是提交(Submit),而不是執行(execute)

(1)客戶端建立一個執行緒任務,即一個Callable或Runnable物件。

(2)客戶端呼叫ExecutorService的submit方法,將任務提交給執行器。

(3)ExecutorService的具體類會將submit委託給AbstractExecutorService(具體類的父類)。

(4)父類submit方法將獲取到的Runnable/Callable任務交由其內部方法newTaskFor進行包裝。

(5)newTaskFor方法將Runnable/Callable包裝成FutureTask物件。

(6)submit把包裝好的FutureTask物件交由execute方法執行,此方法有ThreadPoolExecutor(具體類)提供。

(6)submit方法返回FutreTask物件引用給客戶端。

任務提交為何能接收兩種型別的介面?

FutureTask接收到Runnable物件後,會利用介面卡,將其適配為Callable物件進行使用。注意,Runnable適配後,返回值基本沒什麼意義,都是寫死的。

  

實際上很有意思的是,FutureTask只使用Callable物件(因為使用Future的初衷就是想要獲取任務處理結果),而Executor的execute只接收Runnable物件(執行器只管執行任務)

 FutureTask

FutureTask實際上相當於Runnable物件的裝飾器,FutureTask的繼承結構如圖所示:

    

我們知道Runnable定義了任務該做什麼,Future定義了任務的控制操作,而RunnableFuture介面兼具這兩個功能。

Future就是實現這組操作的實現類,它也是Runnable的裝飾器類,Runnable任務在經過其包裝後,仍然還是Runnable,不影響其交給execute方法執行。而且他實現了Future介面,也就可以根據它對任務進行控制。

FutureTask有哪些欄位,用來做什麼的?

  

(1)state => 狀態,用於基於狀態的控制操作。

  • NEW  => 新建任務
  • COMPLETING => 正在完成,即任務已經被執行緒啟動
  • NORMAL => 正常完成任務
  • EXCEPTIONAL => 任務因為異常而終止
  • CANCELLED => 任務已被取消,注意這裡並不表示任務實際狀態,即任務可能還在執行。
  • INTERRUPTING => 中斷任務中
  • INTERRUPTD => 任務已被中斷

(2)callable => callable任務,實際被執行的任務

(3)outcome => 執行結果

(4)runner => 執行執行緒的引用,用來控制任務的執行。

(5)waiters => 等待執行緒佇列,當任務還未完成時,用於儲存因為獲取結果的而被阻塞的執行緒。

 

FutureTask的狀態變化

(1)NEW -> COMPLETING -> NORMAL(任務正常執行到結束)

(2)NEW -> COMPLETING -> EXCEPTIONAL(任務執行過程中出現異常)

(3)NEW -> CANCELLED (任務被取消)

(4)NEW -> INTERRUPTING -> INTERRUPTED (任務已經開始,尚未完成就被取消)

 

FutureTask如何確定其執行執行緒的?

  任務的控制最主要的兩個功能就是取消和獲取結果。取消的操作,上一篇博文已經講到了,獲取結果將於下篇講述。這裡補充前篇的一些內容,也就是取消操作相關的細節,當時已經獲知,要取消任務,實際上是通過中斷任務的執行執行緒實現的,如圖:

  FutureTask的cancel方法

  

但是,這個runner是何時被賦值的,我當時並不清楚,查閱原始碼也沒發現什麼setRunner之類的程式碼。後來突然想到,只有在任務被執行的時候才能知道,它到底被哪個執行緒執行。於是才注意到了這段CAS的程式碼,(當時不太懂,所以就算看到了這段程式碼,也不明白)。意思就是說,如果當前物件的runner欄位值為null,就將其設定為當前的執行執行緒。到這裡,我們就有了此執行緒的引用。

 

  

 

  

FutureTask到達ThreadPoolExecutor的execute之後,是什麼情形?

  這裡簡要說一下,任務到達ThreadPoolExecutor之後,執行緒池會根據當前執行緒數量的情況進行處理,可能建立一個新執行緒來執行,或者加入到任務佇列等待執行,再或者就是被執行緒池拋棄等等。

  相關細節可檢視,我關於ThreadPoolExecutor的相關博文。

 

 

相關文章