對線程池簡單理解

線程池的好處:segmentfault

1,由於線程是比較昂貴的資源,避免大量重複建立銷燬線程,使用者不用關心建立銷燬線程。服務器

2,用戶提交的任務可以及時的獲得處理,提升響應速度。多線程

3,可以更好的監控和管理線程。函數

ThreadPoolExecutor參數

 

    • int corePoolSize 
      • 線程池基本大小
    • int maximumPoolSize 
      • 線程池最大大小
    • long keepAliveTime 
      • 保持活動時間
    • TimeUnit unit 
      • 保持活動時間單位
    • BlockingQueue workQueue 
      • 工做隊列
    • ThreadFactory threadFactory 
      • 線程工廠
    • RejectedExecutionHandler handler 
      • 駁回回調

這些參數這樣描述起來很空洞,下面結合執行任務的流程來看一下。spa

ThreadPoolExecutor執行任務流程

當線程池大小 >= corePoolSize 且 隊列未滿時,這時線程池使用者與線程池之間構成了一個生產者-消費者模型。線程池使用者生產任務,線程池消費任務,任務存儲在BlockingQueue中,注意這裏入隊使用的是offer,當隊列滿的時候,直接返回false,而不會等待。操作系統

keepAliveTime

當線程處於空閒狀態時,線程池須要對它們進行回收,避免浪費資源。但空閒多長時間回收呢,keepAliveTime就是用來設置這個時間的。默認狀況下,最終會保留corePoolSize個線程避免回收,即便它們是空閒的,以備不時之需。但咱們也能夠改變這種行爲,經過設置allowCoreThreadTimeOut(true).net

workQueue

線程池所使用的緩衝隊列,該緩衝隊列的長度決定了可以緩衝的最大數量,緩衝隊列有三種通用策略:線程

1) 直接提交。工做隊列的默認選項是 SynchronousQueue,它將任務直接提交給線程而不保持它們。在此,若是不存在可用於當即運行任務的線程,則試圖把任務加入隊列將失敗,所以會構造一個新的線程。此策略能夠避免在處理可能具備內部依賴性的請求集時出現鎖。直接提交一般要求無界 maximumPoolSizes 以免拒絕新提交的任務。當命令以超過隊列所能處理的平均數連續到達時,此策略容許無界線程具備增加的可能性;code

2) 無界隊列。使用無界隊列(例如,不具備預約義容量的 LinkedBlockingQueue)將致使在全部 corePoolSize 線程都忙時新任務在隊列中等待。這樣,建立的線程就不會超過 corePoolSize。(所以,maximumPoolSize 的值也就無效了。)當每一個任務徹底獨立於其餘任務,即任務執行互不影響時,適合於使用無界隊列;例如,在 Web 頁服務器中。這種排隊可用於處理瞬態突發請求,當命令以超過隊列所能處理的平均數連續到達時,此策略容許無界線程具備增加的可能性;blog

3) 有界隊列。當使用有限的 maximumPoolSizes 時,有界隊列(如 ArrayBlockingQueue)有助於防止資源耗盡,可是可能較難調整和控制。隊列大小和最大池大小可能須要相互折衷:使用大型隊列和小型池能夠最大限度地下降 CPU 使用率、操做系統資源和上下文切換開銷,可是可能致使人工下降吞吐量。若是任務頻繁阻塞(例如,若是它們是 I/O 邊界),則系統可能爲超過您許可的更多線程安排時間。使用小型隊列一般要求較大的池大小,CPU 使用率較高,可是可能遇到不可接受的調度開銷,這樣也會下降吞吐量。

unit:參數keepAliveTime的時間單位,有7種取值,在TimeUnit類中有7種靜態屬性:

TimeUnit.DAYS;               //天  
TimeUnit.HOURS;             //小時  
TimeUnit.MINUTES;           //分鐘  
TimeUnit.SECONDS;           //秒  
TimeUnit.MILLISECONDS;      //毫秒  
TimeUnit.MICROSECONDS;      //微妙  
TimeUnit.NANOSECONDS;       //納秒 

三種經常使用的 ThreadPoolExecutor

Executors 是提供了一組工廠方法用於建立經常使用的 ExecutorService ,分別是 FixedThreadPool,CachedThreadPool 以及 SingleThreadExecutor。這三種ThreadPoolExecutor都是調用 ThreadPoolExecutor 構造函數進行建立,區別在於參數不一樣。

FixedThreadPool - 線程池大小固定,任務隊列無界

下面是 Executors 類 newFixedThreadPool 方法的源碼:

public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }

能夠看到 corePoolSize 和 maximumPoolSize 設置成了相同的值,此時不存在線程數量大於核心線程數量的狀況,因此KeepAlive時間設置不會生效。任務隊列使用的是不限制大小的 LinkedBlockingQueue ,因爲是無界隊列因此容納的任務數量沒有上限。

所以,FixedThreadPool的行爲以下:

  1. 從線程池中獲取可用線程執行任務,若是沒有可用線程則使用ThreadFactory建立新的線程,直到線程數達到nThreads

  2. 線程池線程數達到nThreads之後,新的任務將被放入隊列

FixedThreadPool的優勢是可以保證全部的任務都被執行,永遠不會拒絕新的任務;同時缺點是隊列數量沒有限制,在任務執行時間無限延長的這種極端狀況下會形成內存問題。

SingleThreadExecutor - 線程池大小固定爲1,任務隊列無界

public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }

這個工廠方法中使用無界LinkedBlockingQueue,並的將線程數設置成1,除此之外還使用FinalizableDelegatedExecutorService類進行了包裝。這個包裝類的主要目的是爲了屏蔽ThreadPoolExecutor中動態修改線程數量的功能,僅保留ExecutorService中提供的方法。雖然是單線程處理,一旦線程由於處理異常等緣由終止的時候,ThreadPoolExecutor會自動建立一個新的線程繼續進行工做。

 

SingleThreadExecutor 適用於在邏輯上須要單線程處理任務的場景,同時無界的LinkedBlockingQueue保證新任務都可以放入隊列,不會被拒絕;缺點和FixedThreadPool相同,當處理任務無限等待的時候會形成內存問題。

CachedThreadPool - 線程池無限大(MAX INT),等待隊列長度爲1

public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }

SynchronousQueue是一個只有1個元素的隊列,入隊的任務須要一直等待直到隊列中的元素被移出。核心線程數是0,意味着全部任務會先入隊列;最大線程數是Integer.MAX_VALUE,能夠認爲線程數量是沒有限制的。KeepAlive時間被設置成60秒,意味着在沒有任務的時候線程等待60秒之後退出。CachedThreadPool對任務的處理策略是提交的任務會當即分配一個線程進行執行,線程池中線程數量會隨着任務數的變化自動擴張和縮減,在任務執行時間無限延長的極端狀況下會建立過多的線程。

 
  1. 爲何newFixedThreadPool中要將corePoolSize和maximumPoolSize設置成同樣? 答:由於newFixedThreadPool中用的是LinkedBlockingQueue(是無界隊列),只要當前線程大於等於corePoolSize來的任務就直接加入到無界隊列中,因此線程數不會超過corePoolSize,這樣maximumPoolSize沒有用。例如,在 Web 頁服務器中。這種排隊可用於處理瞬態突發請求,當命令以超過隊列所能處理的平均數連續到達時,此策略容許無界線程具備增加的可能性。
  2. 爲何newFixedThreadPool中隊列使用LinkedBlockingQueue?答:設置的corePoolSize 和 maximumPoolSize相同,則建立的線程池是大小固定的,要保證線程池大小固定則須要LinkedBlockingQueue(無界隊列)來保證來的任務可以放到任務隊列中,不至於觸發拒絕策略。
  3. 爲何newFixedThreadPool中keepAliveTime會設置成0?由於corePoolSize和maximumPoolSize同樣大,KeepAliveTime設置的時間會失效,因此設置爲0。
  4. 爲何newCachedThreadPool中要將corePoolSize設置成0?答:由於隊列使用SynchronousQueue,隊列中只能存放一個任務,保證全部任務會先入隊列,用於那些互相依賴的線程,好比線程A必須在線程B以前先執行。
  5. 爲何newCachedThreadPool中隊列使用SynchronousQueue?答:線程數會隨着任務數量變化自動擴張和縮減,能夠靈活回收空閒線程,用SynchronousQueue隊列整好保證了CachedTheadPool的特色。
  6. 爲何newSingleThreadExecutor中使用DelegatedExecutorService去包裝ThreadPoolExecutor?答:SingleThreadExecutor是單線程化線程池,用DelegatedExecutorService包裝爲了屏蔽ThreadPoolExecutor動態修改線程數量的功能,僅保留Executor中的方法。

 參考網址:http://blog.csdn.net/ghsau/article/details/53538303

https://segmentfault.com/a/1190000008394155

相關文章
相關標籤/搜索