四種線程池的解析

首先咱們先看一下獲取四種線程池的代碼:html

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);
    ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();

複製代碼

能夠發現這四種線程池都是由Executors類生成的。依次點開四個方法的內部實現發現,它們最終調用的都是同一個ThreadPoolExecutor()的構造器,而區別在於構造器的參數不一樣。咱們來看下ThreadPoolExecutor的參數列表:bash

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
複製代碼

正是因爲這幾個參數的不一樣致使了四種線程池的工做機制不一樣。參考源碼對於參數的註釋,咱們列出參數的含義。併發

  • corePoolSize:核心線程數量,常駐在線程池中的線程,即便它們是空閒的,也不會銷燬,除非設置allowCoreThreadTimeOut的值。
  • maximumPoolSize:線程池最大線程數量
  • keepAliveTime:超過核心數量的額外線程也就是非核心線程,在空閒指定的最大時間後被銷燬。(假設時間爲5s,核心線程數爲2,當前線程爲4,則超過核心線程數的其他兩個線程在空閒5秒後會被銷燬。)
  • unit:時間單位
  • workQueue:等待隊列
  • threadFactory:生成線程的工廠
  • handler:當等待隊列容量滿以及線程池數量達到最大時,如何處理新的任務。
    • AbortPolicy(默認):直接拋出異常
    • CallerRunsPolicy:交給調用者所在線程執行。(假設當前調用者線程是Main,那麼就交給Main處理)
    • DiscardOldestPolicy:丟棄最久未處理的任務,再執行當前任務。(最久未處理的,在隊列中其實就是隊列頭節點,查看源碼的確調用是poll()方法)
    • DiscardPolicy:丟掉該任務,而且不拋異常。

線程池的工做機制:

當持續往線程池添加任務,
當前線程數量小於核心線程數量的時候,新增線程。
當前線程數量達到核心線程數量的時候,將任務放入等待隊列。
當等待隊列滿的時候,繼續建立新線程。
當線程池數量達到最大而且等待隊列也滿的時候,採起拒絕服務策略。ui

接下來咱們就根據參數來分析不一樣的線程池:

FixedThreadPool

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

咱們能夠看到corePoolSize核心線程數量和maximumPoolSize最大線程數量是一致的,而且keepAliveTime爲0。workQueue是LinkedBlockingQueue,這是一個鏈表阻塞隊列。能夠得出結論:該線程池是一個固定數量的線程池,而且有一個無界的等待隊列。咱們能夠推導出該線程池適合處理任務量平穩的場景。例如平均一秒接收10個任務,接收任務量曲線不會很陡峭。spa

適合場景:適合少許的大任務(大任務處理慢,若是線程數量多的話,反而在切換線程上下文時損耗,因此控制線程在必定的數量)。線程

CachedThreadPool

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

咱們能夠看到corePoolSize核心線程池爲0,表明該線程沒有核心線程池,意味着線程都是可被回收銷燬的,線程池中有時會是空的。而且maximumPoolSize是int最大值,至關於表明該線程池能夠無限建立線程。keepAliveTime爲60,表明空閒60秒回收線程。workQueue是SynchronousQueue,該同步隊列是一個沒有容量隊列,即一個任務到來後,要等待線程來消費,才能再繼續添加任務。咱們推導出該線程池適合處理平時沒什麼任務量,但有時任務量瞬間劇增的場景。code

適合場景:大量的小任務(每一個任務處理快,不會頻繁出現線程處理一半時,切換其餘線程)。htm

ScheduledThreadPool

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
複製代碼

咱們能夠看到該線程池參數最大的區別在於workQueue是DelayedWorkQueue。該隊列是一個按延遲時間從小到大排序的堆。而且當隊列頭節點的延遲時間小於0的時候返回該節點。因此該線程池能夠指定一個時間進行定時任務。也能夠經過添加任務時遞增延遲時間,來進行週期任務。排序

適合場景:定時任務或者週期任務。隊列

SingleThreadExecutor

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

咱們能夠看到該線程池的corePoolSize核心線程數量和maximumPoolSize最大線程數量都是1,表明該線程有且只有一個固定的線程,既然是單線程,因此該線程池實現的是串行操做,沒有併發效果。workQueue是LinkedBlockingQueue,這是一個鏈表阻塞隊列。因此該線程池適合執行串行執行隊列中的任務。

適合場景:按順序串行處理的任務。

可能讀者會好奇keepAliveTime爲0表明的含義? 是當即回收線程仍是永不回收呢?

keepAliveTime參數註釋明確指明只對非核心線程有用。 咱們能夠從ScheduledThreadPool的源碼中推測,若是0表明是永不回收的話,那麼ScheduledThreadPool一旦建立出非核心線程的話就不會回收了?這樣是很不合理的。因此筆者認爲0表明當即回收。

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
複製代碼

筆者水平有限,若有錯誤懇請評論指正。

轉自個人我的博客 vc2x.com

相關文章
相關標籤/搜索