ThreadPoolExecutor執行原理

線程池

基本概念: 一種線程使用模式。線程過多會帶來調度開銷,進而影響緩存局部性和整體性能。

不要使用Executor創建線程池,而是通過ThreadPoolExecutor的方式創建,這樣的處理方式能讓編寫代碼的人更加明確線程池的運行規則,規避資源耗盡的風險。

說明:Executors 返回的線程池對象的弊端如下:

1)FixedThreadPool和SingleThreadPool:

允許請求的隊列長度爲Interger.MAX_VALUE,可能會堆積大量的請求,從而導致OOM。

2)CachedThreadPool:

允許的創建線程數量爲:Interger.MAX_VALUE,可能會創建大量的線程,從而導致OOM。

Executor創建線程的3大方法:

//Executor不建議使用,容易引發OOM
ExecutorService threadPool = Executors.newSingleThreadExecutor();//單線程
ExecutorService threadPool1 = Executors.newFixedThreadPool(5);//固定線程
ExecutorService threadPool2 = Executors.newCachedThreadPool();//可變線程

for (int i = 0; i < 10; i++) {
    threadPool2.execute(()->{
        System.out.println(Thread.currentThread().getName());
    });
}

ThreadPoolExecutor的七大參數:

核心線程數,最大線程數,超時等待時間,超時等待單位,阻塞隊列,線程工廠,拒絕策略。

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

執行過程:如果線程數沒有超過核心線程數,那麼就會用到幾個開闢幾個,如果大於核心線程數,但是沒有超過阻塞隊列的個數,那麼就會等待覈心線程執行完,再取阻塞隊列中的執行

測試代碼

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
    2,
    6,
    3,
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(3),
    Executors.defaultThreadFactory(),
    new ThreadPoolExecutor.AbortPolicy()
);
try {
    for (int i = 0; i < 7; i++) {
        final int temp = i;
        threadPoolExecutor.execute(()->
                                   {
                                       try {
                                           System.out.println(Thread.currentThread().getName()+" "+temp);
                                           TimeUnit.SECONDS.sleep(1);
                                       } catch (InterruptedException e) {
                                           e.printStackTrace();
                                       }

                                   });
    }
} catch (Exception e) {
    e.printStackTrace();
} finally {
    threadPoolExecutor.shutdown();
}

調整不同的循環數會出現不同的執行順序,甚至會報錯,具體是什麼原理呢?下面用畫圖的方式描述出來。

執行過程

以核心線程數爲2,最大線程數爲6,阻塞隊列容量爲3 爲例:

  • 如果要執行的任務數小於等於核心線程數:

在這裏插入圖片描述
如圖,藍色爲待執行的任務,黃色爲核心線程,當要執行的任務數小於等於核心線程數時,直接被核心線程接管:

在這裏插入圖片描述

  • 當待執行的任務數大於核心線程數,但是小於核心線程數+阻塞隊列容量時:

在這裏插入圖片描述
在這裏插入圖片描述

在這裏插入圖片描述
task1,task2被核心線程接管,task3進阻塞隊列等待覈心線程執行完畢再執行。

  • 當待執行的任務數大於核心線程數+阻塞隊列容量但小於最大線程數+阻塞隊列容量時:

在這裏插入圖片描述

因爲阻塞隊列已達到最大容量,剩餘的線程(除去核心線程的其他線程)會被啓動,用來直接接管新來的任務,當線程經過keepAliveTime還沒有接到任務,則再次關閉線程。

注意:直接接管新來的任務,而不是從阻塞隊列中取

  • 當待執行的任務數大於了最大線程數+阻塞隊列容量時:

在這裏插入圖片描述
在這裏插入圖片描述

超出數量的任務會被拒絕,具體的拒絕策略有四種:

  • ThreadPoolExecutor.AbortPolicy():該策略是指,當有超出數量的任務來時,會拋出異常

在這裏插入圖片描述

  • ThreadPoolExecutor.CallerRunsPolicy();:超出數量的任務會被調用者線程執行,比如:

在這裏插入圖片描述
因爲是主線程調用的,所以會被主線程接管。

  • ThreadPoolExecutor.DiscardPolicy();:當超出數量的任務來時,不會拋出異常

在這裏插入圖片描述
​ 可以看出當循環次數爲10時,只執行了9個,多出的任務被丟棄。

  • ThreadPoolExecutor.DiscardOldestPolicy();:超出數量的任務會嘗試去和最早執行的線程競爭
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章