Java線程池ThreadPoolExecutor

本文首發於我的微信公衆號《andyqian》, 關注免費獲取Java學習資料java

前言

  多線程一直是Java進階的必修課。在Java中,咱們很早就知道能夠經過 Thread 類和 Runnable 接口來實現多線程。與之有着相似職責的數據庫鏈接,也可經過JDBC建立與使用。但咱們深知不管是數據庫鏈接的建立與銷燬,仍是線程的建立與銷燬,都是一件及其消耗性能的事情。爲了減小這種狀況的發生,前輩們就在思考,是否是能夠複用已有的數據庫鏈接?減小建立,銷燬動做?這就是後來數據庫鏈接池的由來。一樣的,爲了複用線程,也就有了線程池。我一直獨自暗喜,身爲一位幸福的Java程序員,前有Java虛擬機管理內存,後有Doug Lea 大師提供併發庫,鎖機制。簡直幸福的不像話,不過幸福歸幸福,該掌握的仍是須要掌握的,咱們一塊兒來看看今天的主角:ThreadPoolExecutor。程序員

簡介

  在面試過程當中,也時常會遇到一些關於線程池的問題,例如:面試

  1. 線程池中核心參數有哪些?數據庫

  2. 有沒有本身實現過線程池?編程

  3. 若是讓你本身實現線程池,你會怎麼作?微信

多線程

這些問題,其實考覈的就是Java線程池的知識,更具體一點就是對ThreadPoolExector類熟不熟悉,下面代碼是ThreadPoolExector類的全參構造函數。下面咱們就一一對參數進行了解。併發

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.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

其中:ide

  1. corePoolSize:表示該線程池最小的工做線程數。默認狀況下,當須要使用時建立線程,也能夠調用 prestartAllCoreThreads() 方法進行預建立全部的核心線程。函數

  2. maximumPoolSize:表示該線程池最大的線程數量,理論上將其設置爲無限大,就會建立無限多的線程,固然,建立線程的數量最終由系統資源也就是操做系統決定。

  3. keepAliveTime:表示空閒線程的超時時間,(單位爲納秒)。但在構造函數中,單位與unit 參數配合使用,最終轉換爲納秒。

  4. unit:表示空閒線程超時的時間單位,可選值有:java.util.concurrent.TimeUnit中的值,SECONDS(秒),MINUTES(分),HOURS(小時),DAYS(天) 等。

  5. workQueue :表示工做隊列(實際上是一個runnable隊列,在線程池中定義爲Worker),其基類爲:java.util.concurrent.BlockingQueue。

  6. threadFactory:線程工廠,一般用於建立線程,以及命令規則。默認爲: Executors.defaultThreadFactory()。

  7. handler 表示處理策略,當workQueue隊列滿時,以及建立線程錯誤時的處理策略。其基類爲 java.util.concurrent.RejectedExecutionHandler。默認爲:AbortPolicy 策略。

不一樣組合

  在 java.util.concurrent.Executors類爲咱們提供了多種組合,其底層仍是調用ThreadPoolExecutor。下面列舉幾個經常使用的方法:

1. newFixedThreadPool

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

特性:線程數量大小固定,且 corePoolSize 與 maximumPoolSize 數量相等。當線程數量設置太少時。task則會積壓在LinkedBlockingQueue隊列中。當 task 任務大於Integer.MAX_VALUE時 則會有OOM發生的風與之類是的還有newSingleThreadExecutor方法。

2. newCachedThreadPool

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

特性:corePoolSize數量爲0,maximumPoolSize數量爲Integer.MAX_VALUE,也就是說理論上是能夠建立Integer.MAX_VALUE個線程的。keepAiveTime時間爲 60秒。BlockingQueue使用的是SynchronousQueue,因爲其沒用容量,意味這每一次put對應着一次take操做,其吞吐量比較高。正由於如此,當task到達必定程度時,可能會建立許多線程,從而致使OOM,甚至服務不可用。

 

上述方法實際上是對ThreadPoolExecutor方法的封裝對不對,知道了ThreadPoolExecutor的每個參數,再來使用這個,就駕輕就熟了對不對。

規範

Executors類這麼方便,是否是能夠直接使用Executors類來建立呢?固然能夠,但並不建議這樣作。在《阿里Java手冊》中的併發處理小節中有提到:

【強制】線程池不容許使用 Executors 去建立,而是經過 ThreadPoolExecutor 的方式,這樣的處理方式可讓寫的同窗更加明確線程池的運行規則,規避資源耗盡的風險。

說明:Executors 返回的線程池對象的弊端以下:
1) FixedThreadPool 和 SingleThreadPool :
容許的請求隊列長度爲 Integer.MAX_VALUE,可能會堆積大量的請求,從而致使 OOM 。
2) CachedThreadPool 和 ScheduledThreadPool :容許的建立線程數量爲 Integer.MAX_VALUE ,可能會建立大量的線程,從而致使 OOM 。

在實際應用中,咱們應該遵照規範,避免掉一些不必的問題。該規約其最終目的是讓你們可以更清楚瞭解線程池的每一個參數,從而達到可以在實際應用場景中調爲最優組合使用,使其達到最大性能。一樣的,咱們也經過安裝阿里巴巴的規約插件進行自動掃描與提醒。在Idea中 File -> Setings -> Plugins -> Browse repositories中搜索『Alibaba Java Coding Guidelines』安裝便可!

小結

在這篇文章中,算是對ThreadPoolExector的一個初步瞭解。知道了其核心參數,究竟是怎麼回事。但這還並不夠,且不足以學以至用,還有不少疑問,如:

  1. ThreadPoolExecutor的原理是怎樣的?

  2. 咱們如何自定義一個線程池?

  3. ThreadPoolExecutor在Dubbo中的實踐

這些疑惑都須要一一去揭曉。因爲篇幅緣由,這些會做爲好幾篇文章進行記錄。其目的是可以學以至用,面試時也能駕輕就熟。


 

相關閱讀:

使用 Mybatis 真心不要偷懶!

再談Java 生產神器 BTrace

Java 生產神器  BTrace

重構不徹底指南!


 

若是想深刻學習Java併發編程,《Java併發編程的藝術》這本書是值得閱讀的。固然了,若是你讀喜歡電子書,也能夠回覆公衆號消息『Java併發編程的藝術』進行免費獲取!

 

關注免費獲取Java學習資料

相關文章
相關標籤/搜索