天天看點

任務執行

  大多數并發應用程式都是圍繞“任務執行(Task  Execution)”來構造的:任務通常是一些抽象的且離散的工作單元。

  在生産環境中,“為每個任務配置設定一個線程”這種方法存在一些缺陷,尤其是當需要建立大量線程時:

線程生命周期的開銷非常高。線程的建立與銷毀并不是沒有代價的。

資源消耗。活躍的線程會消耗系統資源,尤其是記憶體。  

穩定性。在可建立線程的數量上存在一個限制。

  在一定範圍内,增加線程可以提高系統的吞吐率,但如果超出了這個範圍,再建立更多的線程隻會降低程式的執行速度,并且如果過多地建立一個線程,那麼整個應用程式将崩潰,要想避免這種危險,就應該對應用程式可以建立的線程數量進行限制,并且全面的測試應用程式,進而確定線上程數量達到限制時,程式也不會耗盡資源。

  在Java類庫中,任務執行的主要抽象不是Thread,而是Executor。雖然Executor是個簡單的接口,但它卻為靈活且強大的異步任務執行架構提供了基礎,該架構能支援多種不同類型的任務執行政策。它提供了一種标準的方法将任務的送出過程與執行過程解耦開來,并用Runnable來表示任務。Executor的實作還提供了對生命周期的支援,以及統計資訊收集、應用程式管理機制和性能監視等機制。

  Executor基于生産者-消費者模式,送出任務的操作相當于生産者(生産待完成的工作單元),執行任務的線程則相當于消費者(執行完這些工作單元)。如果要在程式中實作一個生産者-消費者的設計,那麼最簡單的方式通常就是使用Executor。

  

  線程池,從字面啊啊含義來看,是指管理一組同構工作線程的資源池。線程池是與工作隊列密切相關的,其中在工作隊列中儲存了所有等待執行的任務。工作者線程的任務很簡單:從工作隊列中擷取一個任務,執行任務,然後傳回線程池并等待下一個任務。

  為了解決執行服務的生命周期問題,Executor擴充了ExecutorService接口,添加了一些用于生命周期管理的方法(同時還有一些用于任務送出的便利方法)。ExecutorService的生命周期有3種狀态:運作、關閉和已終止。ExecutorService在初始建立時處于運作狀态。shutdown方法将執行平緩的關閉過程:不再接受新的任務,同時等待已經送出的任務執行完成——包括哪些還未開始執行的任務。shutdownNow方法将執行粗暴的關閉過程:它将嘗試取消所有運作中的任務,并且不再啟動隊列中尚未開始執行的任務。

  CompletionService将Executor和BlockingQueue的功能融合在一起。你可以将Callable任務送出給它來執行,然後使用類似于隊列操作的take和poll等方法來獲得已完成的結果,而這些結果會在完成時将被封裝為Future。ExecutorCompletionService實作了CompletionService,并将計算部分委托給一個Executor。

  有時候,如果某個任務無法在指定時間内完成,那麼将不再需要它的結果,此時可以放棄這個任務。例如,某個web應用程式從外部的廣告伺服器上擷取廣告資訊,但如果該應用程式在兩秒内得不到響應,那麼将顯示一個預設的廣告,這樣即使不能獲得廣告資訊,也不會降低站點的響應性能。在支援時間限制的Future.get中支援這種需求:當結果可用時,它将立即傳回,如果在執行時間内沒有計算出結果,那麼将抛出TimeoutException。