Java線程池的拒絕策略

1、簡介

  jdk1.5 版本新增了JUC併發編程包,極大的簡化了傳統的多線程開發。前面文章中介紹了線程池的使用,連接地址:http://www.javashuo.com/article/p-wgyamqkp-kh.htmlhtml

  Java線程池,是典型的池化思想的產物,相似的還有數據庫的鏈接池、redis的鏈接池等。池化思想,就是在初始的時候去申請資源,建立一批可以使用的鏈接,這樣在使用的時候,就沒必要再進行建立鏈接信息的開銷了。舉個生活中鮮明的例子,在去著名洋快餐某基或者某勞的時候,配餐人員是從一箇中間的保溫箱中直接取食材,而後打包就行了。不用再臨時的來了一個單子,又要去拿原材料,又要去進行加工。效率明顯的就是提升了不少。java

  俗話說 滿而不損則溢,盈而不持則傾。線程池既然是容器,那麼必然的會有存滿的狀況。在達到某些特定條件的時候,再來請求的話,池子是如何進行請求處理的呢?這裏就引出了池的拒絕策略。通常的數據庫鏈接池在達到最大鏈接數的時候會默認的等待特定的設置的時間或者直接就拋出異常。而本文中要闡述的線程池卻並不是如此的策略,下面開始展開講解下。redis

2、線程池的拒絕策略

  線程池中,有三個重要的參數,決定影響了拒絕策略:corePoolSize - 核心線程數,也即最小的線程數。workQueue - 阻塞隊列 。 maximumPoolSize - 最大線程數數據庫

  當提交任務數大於 corePoolSize 的時候,會優先將任務放到 workQueue 阻塞隊列中。當阻塞隊列飽和後,會擴充線程池中線程數,直到達到 maximumPoolSize 最大線程數配置。此時,再多餘的任務,則會觸發線程池的拒絕策略了。編程

  總結起來,也就是一句話,當提交的任務數大於(workQueue.size() + maximumPoolSize ),就會觸發線程池的拒絕策略多線程

3、拒絕策略定義

  拒絕策略提供頂級接口 RejectedExecutionHandler ,其中方法 rejectedExecution 即定製具體的拒絕策略的執行邏輯。併發

  jdk默認提供了四種拒絕策略:性能

                CallerRunsPolicy - 當觸發拒絕策略,只要線程池沒有關閉的話,則使用調用線程直接運行任務。通常併發比較小,性能要求不高,不容許失敗。可是,因爲調用者本身運行任務,若是任務提交速度過快,可能致使程序阻塞,性能效率上必然的損失較大測試

                AbortPolicy - 丟棄任務,並拋出拒絕執行 RejectedExecutionException 異常信息。線程池默認的拒絕策略。必須處理好拋出的異常,不然會打斷當前的執行流程,影響後續的任務執行。spa

                DiscardPolicy - 直接丟棄,其餘啥都沒有

                DiscardOldestPolicy -  當觸發拒絕策略,只要線程池沒有關閉的話,丟棄阻塞隊列 workQueue 中最老的一個任務,並將新任務加入

4、測試代碼

  一、AbortPolicy 

package com.cfang;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

@Slf4j
public class T2 {

    public static void main(String[] args) throws Exception{
        int corePoolSize = 5;
        int maximumPoolSize = 10;
        long keepAliveTime = 5;
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(10);
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue, handler);
        for(int i=0; i<100; i++) {
            try {
                executor.execute(new Thread(() -> log.info(Thread.currentThread().getName() + " is running")));
            } catch (Exception e) {
                log.error(e.getMessage());
            }
        }
        executor.shutdown();
    }
}

   executor.execute()提交任務,因爲會拋出 RuntimeException,若是沒有try.catch處理異常信息的話,會中斷調用者的處理流程,後續任務得不到執行(跑不完100個)。可自行測試下,很容易在控制檯console中能查看到。

  二、CallerRunsPolicy 

    主體代碼同上,更換拒絕策略:

RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();

  運行後,在控制檯console中可以看到的是,會有一部分的數據打印,顯示的是 「main is running」,也即體現調用線程處理。

  三、DiscardPolicy 

    更換拒絕策略

RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardPolicy();

    直接丟棄任務,實際運行中,打印出的信息不會有100條。

  四、DiscardOldestPolicy 

  一樣的,更換拒絕策略:

RejectedExecutionHandler handler = new ThreadPoolExecutor.DiscardOldestPolicy();

  實際運行,打印出的信息也會少於100條。

5、總結

  四種拒絕策略是相互獨立無關的,選擇何種策略去執行,還得結合具體的業務場景。實際工做中,通常直接使用 ExecutorService 的時候,都是使用的默認的 defaultHandler ,也即 AbortPolicy 策略。

相關文章
相關標籤/搜索