線程池創建+拒絕策略


線程池

適合單個任務處理時間比較短

需要處理的任務數量很大

創建方式的選擇:

線程池的創建方法有兩種

  1. 使用Executors線程工具類 ,直接點 newXxxThreadPool (可以new四種)
  2. 一種是如下所示,手動創建線程池

線程池的構造方法:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

關於使用哪一種方法創建,阿里開發手冊中也提到了

第3條規定:線程資源必須通過線程池提供,不允許在應用中自行顯式創建線程

第4條規定:線程池不允許使用Executors創建,而是通過ThreadPoolExecutor的方式創建,這樣的處理方式能讓編寫代碼的攻城獅更加明確線程池的運行規則,規避資源耗盡(OOM)的風險

所以最好使用第二種,采用手動創建線程池的方式

有什么區別和利害關系呢?

直接使用Executors工具類的話 可以創建四種線程,分別是

newFixedThreadPool()	線程數量固定的線程池
newCachedThreadPool()	可緩存線程的線程池
newScheduledThreadPool()	執行定時任務的線程池
newSingleThreadExecutor()	單線程線程池

有什么弊端呢?

先看看newFixedThreadPool()的構造函數

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

LinkedBlockingQueue 任務隊列沒有指定容量,說明可以添加大量的任務,如果大量任務堆積,可能會導致OOM

再看看newCachedThreadPool的構造函數

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

最大線程數設置成了Integer.MAX_VALUE,意味着可以創建大量線程,也可能導致OOM

所以綜上所述,要使用手動創建線程池的方式

手動創建線程池:

線程池的構造方法

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

構造方法好幾個,下面這個是參數最多的

public ThreadPoolExecutor(	  int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler){
    ...
}

參數說明:

corePoolSize - 線程池核心線程的數量。 (長期維持的數量)
maximumPoolSize - 線程池的最大線程數。
keepAliveTime - 當線程數大於核心時,一旦超過此時間,會將多余的空閑線程殺掉
unit - keepAliveTime 的時間單位。
workQueue - 用來儲存等待執行任務的隊列。
threadFactory - 線程工廠。
handler - 拒絕策略。可以選擇指定的,也可以自定義拒絕策略,實現接口即可

參數詳細說明:

corePoolSize:
	是線程池中長期保持的線程數量
maximumPoolSize:
	線程池能夠處理的最大線程數量,超過就要執行拒絕策略了(默認為報錯)
keepAliveTime:
	當線程池比較閑的時候,超過10s鍾的時間可以把最大的線程數中不用的線程減掉,恢復成核心線程數的數量
	
(java中也有優化機制,當任務比較多的時候,會去新創建一些線程 執行任務;比較空閑的時候,會把新創建		的臨時線程舍棄掉,最終會保持和核心線程數相等的線程數量)

執行流程:

當有任務的時候,先判斷當前是否有空閑線程,有的話則執行

沒有空閑流程的話,看是否可存入workQueue等待隊列

隊列沒有滿的話添加,加入隊列中,排隊等待執行

如果隊列滿了,再看是否超過了 最大的線程數,如果沒超過 直接創建線程執行

如果超過了最大線程數,會執行拒絕策略

拒絕策略:

AbortPolicy: 丟棄任務並拋出RejectedExecutionException異常。 (默認)
DiscardPolicy:也是丟棄任務,但是不拋出異常。
DiscardOldestPolicy:忽略最早的任務(把最早添加任務到隊列的任務忽略掉,然后執行當前的任務)
CallerRunsPolicy:把超出的任務交給當前線程執行
				  本來是線程池自己執行的,結果處理不過來就交給當前的主線程處理

演示幾種拒絕策略

public static void main(String[] args) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, //核心線程數
            4, //最大線程數
            10, //空閑等待時間
            TimeUnit.SECONDS, //空閑等待時間的單位
            new LinkedBlockingQueue<>(3), //等待隊列的長度
            new ThreadPoolExecutor.AbortPolicy() //拒絕策略,拋異常
    );
    for (int i = 0; i < 8; i++) {
        executor.execute(()->{
            System.out.println(Thread.currentThread().getName());
        });
    }
    
}

執行之后發現:

直接拋出異常了,沒有沒有拋出異常,說明可能是某個人物執行的比較快

最大線程數 + 隊列中的數量 就是該線程池能承受的最大任務量 也就是 7

2.DiscardPolicy 是不會報出任何錯的,只做忽略操作

3.DiscardOldestPolicy 執行忽略最早的,也是執行六次

4.CallerRunsPolicy 執行不了的交給主線程處理

main
pool-1-thread-1
pool-1-thread-2
pool-1-thread-1
pool-1-thread-1
pool-1-thread-3
pool-1-thread-2
pool-1-thread-4

自定義拒絕策略:

構造方法中,原本設置拒絕策略的參數,就不要設置了,在設置拒絕參數的位置

設置自定義的拒絕策略,可以使用匿名內部類 ,new RejectedExecutionHandler

public static void main(String[] args) {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2,
            4,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(3),
            new RejectedExecutionHandler() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                    System.out.println("自定義拒絕策略執行了...");
                }
            }
    );
    for (int i = 0; i < 8; i++) {
        executor.execute(()->{
            System.out.println(Thread.currentThread().getName());
        });
    }
}

打印結果:

自定義拒絕策略執行了...
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-1
pool-1-thread-4
pool-1-thread-2
pool-1-thread-3


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM