接上篇JDK中線程中實現同步等待閉環的一種方式 - 池塘里洗澡的鴨子 - 博客園 (cnblogs.com),為什么使用了FutureTask中的get方法就可以實現線程的同步等待?這就將重點講述下FutureTask這個類了,實際上Future接口和實現Future接口的FutureTask類,代表異步計算的結果。其UML類繼承圖,如下:
從上圖看FutureTask除了實現Future接口外,還實現了Runnable接口。因此,FutureTask可以交給Executor執行,也可以由調用線程直接執行(FutureTask.run())。根據FutureTask.run()方法被執行的時機,FutureTask可以處於下面3種狀態(線程狀態轉換參考線程基本方法及其對線程狀態的影響 - 池塘里洗澡的鴨子 - 博客園 (cnblogs.com)):
1)未啟動。FutureTask.run()方法還沒有被執行之前,FutureTask處於未啟動狀態。當創建一個FutureTask,且沒有執行FutureTask.run()方法之前,這個FutureTask處於未啟動狀態。
2)已啟動。FutureTask.run()方法被執行的過程中,FutureTask處於已啟動狀態。
3)已完成。FutureTask.run()方法執行完后正常結束,或被取消(FutureTask.cancel(…)),或執行FutureTask.run()方法時拋出異常而異常結束,FutureTask處於已完成狀。
當FutureTask處於未啟動或已啟動狀態時,執行FutureTask.get()方法將導致調用線程阻塞;當FutureTask處於已完成狀態時,執行FutureTask.get()方法將導致調用線程立即返回結果或拋出異常——非常類似於閉鎖的語義。
看看get()方法的實現:
通過源碼可以看到:get()方法使用AQS類型的同步狀態來持有任務的狀態——運行、完成或者取消(state變量),同時利用state變量也維護了一些額外的狀態變量來持有計算的結果或者拋出異常。與利用AQS類實現同步器的類是不是似曾相識(#^.^#),所以FutureTask雖然代表異步執行的結果,但是可以通過get方法阻塞當前正在運行的線程實現同步等待異步結果。
其還維護一個WaitNode q 指向正在運行計算任務的線程(當前正處於運行狀態),這樣任務被取消就可以終端該線程:
get()方法的執行示意圖如下圖上半部分: