閱讀138 返回首頁    go 技術社區[雲棲]


任務執行(第六章)

任務執行

任務邊界:當圍繞“任務執行”來設計應用程序時,第一步就是要找出清晰的任務邊界。

1.為每個任務創建一個線程的風險:

  1. 線程生命周期的開銷非常高:線程的創建於銷毀
  2. 資源消耗:活躍的線程會消耗係統資源,尤其是內存。如果可運行的線程數量多於可用處理器的數量,那麼有些線程將閑置。所以,如果已經擁有足夠多的線程使CPU保持忙碌狀態,那麼創建再多的線程反而會降低性能
  3. 穩定性:在可創建線程的數量上存在一定限製,這個閑置值將隨平台的不同而不同,並且受多個因素閑置。如果破壞了這些限製,那麼可能拋出OutOfMemoryError異常

    應該對程序中創建的線程數量進行限製。
    

2. Executor框架

    在Java中,執行任務的主要抽象不是Thread,而是Executor。
public interface Executor {
    void execute(Runnable command);
}

Executor框架能支持多種不同類型的任務執行策略,它提供了一種標準的方法將任務的提交過程與執行過程解耦,並用Runnable來表示任務。Executor的實現還提供了對生命周期的支持,以及統計信息收集、應用程序管理機製和性能監視等機製。
Executor基於生產者----消費者模式,提交任務的操作相當於生產者,執行任務的線程相當於消費者。
1. Executor的生命周期

JVM隻有在所有(非守護)線程都終止後才會退出,因此,如果Executor沒有正確關閉,那麼JVM將無法退出。
Executor執行的任務有4個證明周期階段:創建、提交、開始和完成。已提交但尚未開始的任務可以取消,但對於已經開始執行的任務,隻有當他們響應中斷時才能取消。
Executor擴展了ExecutorService接口,用於管理生命周期:

//Executor的生命周期有3中運行狀態:運行、關閉和已終止。
public interface ExecutorService extends Executor {
    /shutdown執行平穩的關閉過程:不再接受新任務,同時等待已提交的任務執行完----包括那些還未開始執行的任務/
    void shutdown();
    /shutdownNow將執行粗暴的關閉過程:它將嚐試取消所有運行中的任務,並且不再啟動隊列中尚未開始執行的任務/
    List<Runnable> shutdownNow();
    boolean isShutdown();
    boolean isTerminated();
    boolean awaitTermination(long timeout,TimeUnit unit) throws InterruptedExecutor;
}

Executor的缺陷:
1. Executor使用Runnable作為基本的任務標誌形式,但Runnable中的run()不能返回值也不能拋出異常

3.執行策略

在執行策略中定義了任務執行的“What、Where、When、How”等方麵。包括:
1. 在什麼(what)線程中執行
2. 任務按照什麼順序(FIFO、LIFO、優先級)執行
3. 有多少個(how many)任務能並發執行
4. 在隊列中有多少個(how many)任務等待執行
5. 如果係統由於過載而需要拒絕一個任務,那麼應該選擇哪一個(which)任務?另外如何(how)通知應用程序有任務被拒絕
6. 在執行一個任務之前或之後,應該進行哪些(what)動作

4.線程池

線程池指管理一組同構工作線程的資源池。
可以通過調用Executor中的一些靜態函數創建線程池:
1. newFixedThreadPool:創建一個固定長度的線程池,每當提交一個任務時就創建一個線程,直到創建的線程數量達到最大,此後線程的數量不會再變化。如果某個線程由於發生未預期的Exception而結束,那麼線程會補充一個新的線程
2. newCachedThreadPool:創建一個可緩存的線程池,如果線程池的規模超過了處理需求,那麼將回收空閑的線程,而當需求增加時,則可以添加新的線程,線程池的規模不存在人任何限製
3. newSingleThreadExecutor:單線程的Executor,它創建單個工作者線程來執行任務,如果這個線程異常結束,會創建一個新的線程來代替,其能確保依照任務在隊列中的順序串行序執行
4. new ScheduledThreadPool:創建一個固定長度的線程池,而且以延時或定時的方式來執行順序

    “在線程池中執行任務”要比“為每個任務分配一個線程”優勢更多。

5.延遲任務與周期任務

Timer類負責管理延遲任務以及周期任務,但是Timer類存在一些缺陷,應該考慮使用ScheduledThreadPool類代替它
Timer類的缺陷
1. Timer在執行所有定時任務時隻會創建一個線程,如果某個任務的延時時間過長,那麼將破壞其他TimerTask的定時精確性。
2. 如果TimerTask拋出了一個未受檢查的異常,由於Timer線程並不捕獲異常,因此Timer將終止線程的執行。

    如果要構建自己的調度服務,那麼可以使用DelayQueue,它實現了BlockingQueue,並為ScheduledThreadPoolExecutor提供調度功能。DelayQueue管理著一組Delayed對象,每個Delayed對象都有一個相應的延遲時間:在DelayQueue中,隻有某個元素逾期後,才能從DelayQueue中執行take操作,從DelayQueue中返回的對象將根據他們的延遲時間進行排序。

6.Callable與Future

Callable提供了一種相比Runnable更好的抽象:call,call()有返回值(要用Callable表示無返回值的任務,使用Callable),並且允許拋出異常。
Future表示一個任務的生命周期,並提供了相應的方法來判斷是否已經完成或取消,以及獲取任務的結果和取消任務等。Future意味著任務的生命周期隻能前進,不能後退
如果任務拋出了異常,那麼get將異常封裝為ExecutorException並重新拋出,並且可以通過getCause來獲得被封裝的初始異常,如果任務被取消,那麼get將拋出 CancellationException

public interface Callable<V> {
    V call() throws Exception;
}
public interface Future<V> {
    boolean cancel(boolean mayInterruptIfRunning);
    boolean isCancelled();
    boolean isDone();
    V get() throws InterruptedException,ExecutionException,
            CancellationException;
    V get(long timeOut,TimeUnit unit) throws InterruptedException,ExecutionException,
            CancellationException,TimeoutException;
}
    隻有當大量相互獨立且同構的任務可以並發處理時,才能體現出將程序的工作負載分配到多個任務中帶來的性能提升。

最後更新:2017-11-04 18:03:47

  上一篇:go  取消與關閉(第七章)
  下一篇:go  Java中的基礎構建模塊(第五章)