解開Future的神祕面紗之任務執行
此文承接之前的博文 解開Future的神祕面紗之取消任務 補充一些任務執行的一些細節,並從全域性介紹程式的執行情況。
任務提交到執行的流程
前文我們已經瞭解到一些Future的實現細節,這裡我們來梳理一下執行流程。這裡只說sumbit(提交)
(1)客戶端建立一個Callable或Runnable物件。
(2)客戶端呼叫ExecutorService的submit方法,將任務提交給執行器。
(3)ExecutorService將呼叫其實現類AbstractExecutorService的submit方法。
(4)submit方法將獲取到的Runnable/Callable任務交由其內部方法newTaskFor進行包裝。
(5)newTaskFor方法將Runnable/Callable包裝成FutureTask物件。
(6)submit把包裝好的FutureTask物件交由execute方法執行,此方法有ThreadPoolExecutor提供。
(7)submit方法返回FutreTask物件給客戶端。
任務提交為何能接收兩種型別的介面?
實際上很有意思的是,FutureTask只使用Callable物件(因為使用Future的初衷就是想要獲取任務處理結果),而Executor的execute只接收Runnable物件(執行器只管執行任務)
FutureTask接收到Runnable物件後,會利用介面卡,將其適配為Callable物件進行使用。注意,Runnable適配後,返回值基本沒什麼意義,都是寫死的。
而FutureTask實際上相當於Runnable物件的裝飾器,FutureTask的繼承結構如圖所示:
我們知道Runnable定義了任務該做什麼,Future定義了任務的控制操作,而RunnableFuture介面兼具這兩個功能。
Future就是實現這組操作的實現類,它也是Runnable的裝飾器類,Runnable任務在經過其包裝後,仍然還是Runnable,不影響其交給execute方法執行。而且他實現了Future介面,也就可以根據它對任務進行控制。
FutureTask有哪些欄位,用來做什麼的?
(1)state => 狀態,用於基於狀態的控制操作。
NEW => 新建任務
COMPLETING www.tygj178.com=> 正在完成,即任務已經被執行緒啟動
NORMAL => 正常完成任務
EXCEPTIONAL => 任務因為異常而終止
CANCELLED => 任務已被取消,注意這裡並不表示任務實際狀態,即任務可能還在執行。
INTERRUPTING => 中斷任務中
INTERRUPTD => 任務已被中斷
(2)callable www.ysyl157.com=> 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方法
// The Vue build version to load with the `import` command
// (runtime-only or standalone)www.xinghenyule.com has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import boostrap from 'bootstrap/dist/css/bootstrap.css'
import store from 'www.ylouyi3.com./store/index.js'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,//在全域性物件上載入倉庫
components: { App },
template: 'www.dasheng178.com<App/>'
})
兩個元件
+ View Code
navbar.vue
+ View Code
product.vue 該元件用於顯示商品的詳細資訊
+ View Code
程式入口APP.vue
<template>
<div id="app">
<nav-bar>www.yinmaoyule178.com</nav-bar>
<router-view>www.chaoyueyule.net</router-view>
</div>
</template>
<script>
import NavBar from 'www.dfgjpt.com/./views/components/navbar'
export default {
name: 'App',
components:{NavBar}
}
</script>
但是,這個runner是何時被賦值的,我當時並不清楚,查閱原始碼也沒發現什麼setRunner之類的程式碼。後來突然想到,只有在任務被執行的時候才能知道,它到底被哪個執行緒執行。於是才注意到了這段CAS的程式碼,(當時不太懂,所以就算看到了這段程式碼,也不明白)。意思就是說,如果當前物件的runner欄位值為null,就將其設定為當前的執行執行緒。到這裡,我們就有了此執行緒的引用。
FutureTask到達ThreadPoolExecutor的execute之後,是什麼情形?
這裡簡要說一下,任務到達ThreadPoolExecutor之後,執行緒池會根據當前執行緒數量的情況進行處理,可能建立一個新執行緒來執行,或者加入到任務佇列等待執行,再或者就是被執行緒池拋棄等等。
相關細節可檢視,我關於ThreadPoolExecutor的相關博文。