Java線程池ThreadPoolExecutor初略探索

在操做系統中,線程是一個很是重要的資源,頻繁建立和銷燬大量線程會大大下降系統性能。Java線程池原理相似於數據庫鏈接池,目的就是幫助咱們實現線程複用,減小頻繁建立和銷燬線程
ThreadPoolExecutor圖解java

ThreadPoolExecutor

ThreadPoolExecutor是線程池的核心類。首先看一下如何建立一個ThreadPoolExecutor。下面是ThreadPoolExecutor經常使用的一個構造方法:數據庫

構造方法

ThreadPoolExecutor構造方法

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

使用案例併發

/**
  * 阻塞的線程池
  */
private ThreadPoolExecutor executor = new ThreadPoolExecutor(
    0,      // corePoolSize:線程池維護線程的最少數量
    4,      // maximumPoolSize:線程池維護線程的最大數量
    10000,  // keepAliveTime:線程池維護線程所容許的空閒時間
    TimeUnit.MILLISECONDS,          // unite:線程池維護線程所容許的空閒時間的單位
    new LinkedBlockingQueue<>(200), // workQueue:線程池所使用的緩衝隊列
    new CallerBlockedPolicy()       // handler:線程池對拒絕任務的處理策略,自定義拓展
  );

構造方法參數說明

  • corePoolSize:核心線程數量,當有新任務在execute()方法提交時,會執行如下判斷:
    • 若是運行的線程少於 corePoolSize,則建立新線程來處理任務,即便線程池中的其餘線程是空閒的;
    • 若是線程池中的線程數量大於等於 corePoolSize 且小於 maximumPoolSize,則只有當workQueue滿時才建立新的線程去處理任務;
    • 若是設置的corePoolSizemaximumPoolSize相同,則建立的線程池的大小是固定的,這時若是有新任務提交,若workQueue未滿,則將請求放入workQueue中,等待有空閒的線程去從workQueue中取任務並處理;
    • 若是運行的線程數量大於等於maximumPoolSize,這時若是workQueue已經滿了,則經過handler所指定的策略來處理任務;
      因此,任務提交時,判斷的順序爲 corePoolSize –> workQueue –> maximumPoolSize。
  • maximumPoolSize:最大線程數量;
  • workQueue:等待隊列,當任務提交時,若是線程池中的線程數量大於等於corePoolSize的時候,把該任務封裝成一個Worker對象放入等待隊列;
  • workQueue:保存等待執行的任務的阻塞隊列,當提交一個新的任務到線程池之後, 線程池會根據當前線程池中正在運行着的線程的數量來決定對該任務的處理方式,主要有如下幾種處理方式:
    • 直接切換:這種方式經常使用的隊列是SynchronousQueue,但如今尚未研究過該隊列,這裏暫時還無法介紹;
    • 使用無界隊列:通常使用基於鏈表的阻塞隊列LinkedBlockingQueue。若是使用這種方式,那麼線程池中可以建立的最大線程數就是corePoolSize,而maximumPoolSize就不會起做用了(後面也會說到)。當線程池中全部的核心線程都是RUNNING狀態時,這時一個新的任務提交就會放入等待隊列中。
    • 使用有界隊列:通常使用ArrayBlockingQueue。使用該方式能夠將線程池的最大線程數量限制爲maximumPoolSize,這樣可以下降資源的消耗,但同時這種方式也使得線程池對線程的調度變得更困難,由於線程池和隊列的容量都是有限的值,因此要想使線程池處理任務的吞吐率達到一個相對合理的範圍,又想使線程調度相對簡單,而且還要儘量的下降線程池對資源的消耗,就須要合理的設置這兩個數量。
      • 若是要想下降系統資源的消耗(包括CPU的使用率,操做系統資源的消耗,上下文環境切換的開銷等), 能夠設置較大的隊列容量和較小的線程池容量, 但這樣也會下降線程處理任務的吞吐量。
      • 若是提交的任務常常發生阻塞,那麼能夠考慮經過調用 setMaximumPoolSize() 方法來從新設定線程池的容量。
      • 若是隊列的容量設置的較小,一般須要將線程池的容量設置大一點,這樣CPU的使用率會相對的高一些。但若是線程池的容量設置的過大,則在提交的任務數量太多的狀況下,併發量會增長,那麼線程之間的調度就是一個要考慮的問題,由於這樣反而有可能下降處理任務的吞吐量。
  • keepAliveTime:線程池維護線程所容許的空閒時間。當線程池中的線程數量大於corePoolSize的時候,若是這時沒有新的任務提交,核心線程外的線程不會當即銷燬,而是會等待,直到等待的時間超過了keepAliveTime
  • threadFactory:它是ThreadFactory類型的變量,用來建立新線程。默認使用Executors.defaultThreadFactory() 來建立線程。使用默認的- - - - ThreadFactory來建立線程時,會使新建立的線程具備相同的NORM_PRIORITY優先級而且是非守護線程,同時也設置了線程的名稱。
  • handler:它是RejectedExecutionHandler類型的變量,表示線程池的飽和策略。若是阻塞隊列滿了而且沒有空閒的線程,這時若是繼續提交任務,就須要採起一種策略處理該任務。線程池提供了4種策略:
    • AbortPolicy:直接拋出異常,這是默認策略;
    • CallerRunsPolicy:用調用者所在的線程來執行任務;
    • DiscardOldestPolicy:丟棄阻塞隊列中靠最前的任務,並執行當前任務;
    • DiscardPolicy:直接丟棄任務;

線程池的監控

經過線程池提供的參數進行監控。線程池裏有一些屬性在監控線程池的時候可使用性能

  • getTaskCount:線程池已經執行的和未執行的任務總數;
  • getCompletedTaskCount:線程池已完成的任務數量,該值小於等於taskCount;
  • getLargestPoolSize:線程池曾經建立過的最大線程數量。經過這個數據能夠知道線程池是否滿過,也就是達到了 maximumPoolSize;
  • getPoolSize:線程池當前的線程數量;
  • getActiveCount:當前線程池中正在執行任務的線程數量。

總結一下線程池添加任務的整個流程:this

  1. 線程池剛剛建立是,線程數量爲0;
  2. 執行execute添加新的任務時會在線程池建立一個新的線程;
  3. 當線程數量達到corePoolSize時,再添加新任務則會將任務放到workQueue隊列;
  4. 當隊列已滿放不下新的任務,再添加新任務則會繼續建立新線程,但線程數量不超過maximumPoolSize;
  5. 當線程數量達到maximumPoolSize時,再添加新任務則會拋出異常。
相關文章
相關標籤/搜索