[java]線程池創建ThreadPoolExecutor

【強制】線程池不允許使用 Executors 去創建,而是通過 ThreadPoolExecutor 的方式,這樣的處理方式讓寫的同學更加明確線程池的運行規則,規避資源耗盡的風險。
說明: Executors 返回的線程池對象的弊端 如下
1 FixedThreadPool 和 SingleThread Pool
允許的請求隊列長度爲 Integer.MAX_VALUE,可 能會堆積大量的請求,從而導致 OOM。
2 CachedThreadPool 和 ScheduledThreadPool
允許的創建線程數量爲 Integer.MAX_VALUE 可能會創建大量的線程,從而導致 OOM。

1、線程池解決了哪些問題?

(1)複用已有的線程資源;
(2)可以對任務限流,控制線程數量,使用不同的任務拒絕策略;
(3)降低頻繁的創建和銷燬線程;

2、ThreadPoolExecutor構造函數

取自全參數的構造函數代碼。

 /*
 *使用給定的初始名稱創建一個新的{@code ThreadPoolExecutor}參數。
*
* @param corePoolSize保留在池中的線​​程數,即使如果它們空閒,除非設置了{@code allowCoreThreadTimeOut}
* @param maximumPoolSize在允許的最大線程數池
* @param keepAliveTime當線程數大於核心,這是多餘的空閒線程的最長時間將終止之前等待新任務。
* @param unit {@code keepAliveTime}參數的時間單位
* @param work將隊列用於保存任務之前將其暫存已執行。此隊列將僅容納{@code Runnable}
* 由{@code execute}方法提交的任務。
* @param threadFactory執行程序時要使用的工廠創建一個新線程
* @param handler阻止執行時使用的處理程序因爲達到了線程界限和隊列容量
* @throws IllegalArgumentException如果以下條件之一成立:<br>
* {@code corePoolSize <0} <br>
* {@code keepAliveTime <0} <br>
* {@code maximumPoolSize <= 0} <br>
* {@code maximumPoolSize <corePoolSize}
*如果{@code workQueue},則拋出NullPointerException
*或{@code threadFactory}或{@code handler}爲空
 */
 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;
    }

3、 線程池執行流程圖

在這裏插入圖片描述

4、參數解釋

參數 類型 解釋
corePoolSize int 核心線程數,如果allowCoreThreadTimeOut爲false,即使線程處於空閒狀態,也會保留在池中。如果爲true,則根據keepAliveTime剔除。
maximumPoolSize int 最大線程數
keepAliveTime long 如果線程池當前擁有超過corePoolSize的線程數,那麼多餘的線程在空閒時間超過keepAliveTime時會被終止 。
unit TimeUnit 時間單位
workQueue BlockingQueue 線程等待隊列
threadFactory ThreadFactory 執行程序時要使用工廠創建一個新線程
handler RejectedExecutionHandler 拒絕策略,當達到了線程界面和隊列容量,要使用的處理程序

5、部分參數詳細解釋

5.1、workQueue 等待隊列

用於存放提交的任務,隊列的實際容量與線程池穩定性相關聯。
如果當前線程池任務線程數量小於核心線程池數量,執行器總是優先創建一個任務線程,而不是從線程池中取一個空閒線程。
如果當前線程池任務線程數量大於核心線程池數量,執行器總是優先從線程隊列中取一個空閒線程,而不是創建一個任務線程。
如果當前線程池任務線程數量大於核心線程池數量,且池中無空閒任務線程,將會創建一個任務線程,直到超出maximumPoolSize,如果超出maximumPoolSize,則任務將會被拒絕。

主要有三種隊列策略:

(1)Direct handoffs 直接握手隊列

Direct handoffs的默認選擇是 SynchronousQueue,它將任務直接交給線程而不是自己保留。如果沒有線程能立即運行它,將構建新的線程。Direct handoffs通常需要無限制的maximumPoolSizes來避免拒絕新提交的任務。
注意:當任務持續以平均提交速度大餘平均處理速度時,會導致線程數量會無限增長問題。

(2)Unbounded queues 無界隊列(例如未定義容量的LinkedBlockingQueue)

當所有corePoolSize線程繁忙時,使用無界隊列將導致新任務在隊列中等待,從而導致maximumPoolSize的值沒有任何作用,從而導致OOM。

(3)Bounded queues 有界隊列(例如ArrayBlockingQueue)

一個有界的隊列和有限的maximumPoolSizes配置有助於防止資源耗盡,但是難以控制。隊列大小和maximumPoolSizes需要相互權衡。

5.2、threadFactory 線程工廠

默認爲Executors.defaultThreadFactory()。
通過提供不同的ThreadFactory,您可以更改線程的名稱,線程組,優先級,守護線程狀態等。

5.3、RejectedExecutionHandler 拒絕策略

拒絕任務有兩種情況:
(1) 線程池已經被關閉;
(2)任務隊列已滿且maximumPoolSizes已滿;

void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

預定義了四種處理策略:
AbortPolicy:默認策略,拋出RejectedExecutionException運行時異常;
CallerRunsPolicy:調用當前線程池的所在的線程去執行被拒絕的任務;
DiscardPolicy:直接丟棄新提交的任務;
DiscardOldestPolicy:如果執行器沒有關閉,隊列頭的任務將會被丟棄,然後執行器重新嘗試執行任務(如果失敗,則重複這一過程);
我們可以自己定義RejectedExecutionHandler,以適應特殊的容量和隊列策略場景中。

6、Executors預定義線程池

6.1 newFixedThreadPool

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

隊列:workQueue爲LinkedBlockingQueue(無界阻塞隊列),隊列最大值爲Integer.MAX_VALUE。如果任務提交速度持續大餘任務處理速度,會造成隊列大量阻塞。因爲隊列很大,很有可能在拒絕策略前,內存溢出。
適用場景:可用於Web服務瞬時削峯,但需注意長時間持續高峯情況造成的隊列阻塞。

6.2 newCachedThreadPool

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

隊列:workQueue 爲 SynchronousQueue 同步隊列,這個隊列類似於一個接力棒,入隊出隊必須同時傳遞,因爲CachedThreadPool線程創建無限制,不會有隊列等待,所以使用SynchronousQueue;
適用場景:快速處理大量耗時較短的任務,如Netty的NIO接受請求時,可使用CachedThreadPool。

6.3 newSingleThreadExecutor

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

6.4 newScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }


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

ScheduledThreadPoolExecutor可以用來在給定延時後執行異步任務或者週期性執行任務,相對於任務調度的Timer來說,其功能更加強大,Timer只能使用一個後臺線程執行任務,而ScheduledThreadPoolExecutor則可以通過構造函數來指定後臺線程的個數。

6.5 newWorkStealingPool

public static ExecutorService newWorkStealingPool(int parallelism) {
    return new ForkJoinPool
        (parallelism,
         ForkJoinPool.defaultForkJoinWorkerThreadFactory,
         null, true);
}

創建一個線程池,該線程池維護足夠的線程以支持給定的並行級別,並且可以使用多個隊列來減少爭用。並行級別對應於活動參與或可用參與任務處理的最大線程數。實際的線程數可能會動態增長和收縮。工作竊取池無法保證提交的任務的執行順序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章