1. 程式人生 > >解開Future的神祕面紗之任務執行

解開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的相關博文。