「視頻版」當線程池溢出以後,程序會奔潰嗎?面試突擊 007 期

本文的面試題是,當線程池的任務溢出以後,程序會奔潰嗎?
這個問題問的是關於線程池的任務數超過線程池的承載能力以後,會出現什麼狀況?
那麼,咱們本文就手擼模擬一個線程池溢出的狀況,來看程序的執行狀況。
涉及知識點java

  1. 核心線程數和最大線程數有什麼區別?
  2. 如何模擬線程池溢出?
  3. 拒絕策略的執行流程是什麼?
  4. 什麼是線程池的拒絕策略?
  5. Java 自帶的拒絕策略有哪些?
  6. 如何自定義拒絕策略?
  7. 視頻面試答案

視頻內容(由於視頻比較大,分紅了兩個視頻來展現 ):面試

圖文面試答案ide

當線程池的任務溢出以後,程序並不會奔潰,這時候會觸發線程池的拒絕策略,Java 自帶的拒絕策略有四種:
AbortPolicy:終止策略,線程池終止執行,並直接拋出異常,Java 默認此拒絕策略;
CallerRunsPolicy:把任務交給當前線程執行(原本是線程池本身要執行的,結果處理不過來就交給當前的主線程去處理);
DiscardPolicy:忽略此任務(最新的任務);
DiscardOldestPolicy:忽略最先的任務(最久的任務)。
拒絕策略的執行流程比較繞,這是由於線程池有三個重要的參數:核心線程數(corePoolSize)、最大線程數(maximumPoolSize)、線程池的任務隊列(BlockingQueue),大部分搞不清楚核心線程數和最大線程數有什麼區別?
核心線程數是指在正常狀況下線程池內的線程數量;而最大線程數指的是當線程池的任務隊列存儲超過最大值以後,能夠建立最多的線程數量。
當任務比較少的時候,線程數量會根據設置的超時時間,迴歸線程的數量爲核心線程數量,這個時候最大線程數就暫時沒用了(沒有發揮的餘地了)。
拒絕策略的執行流程是:當提交的任務數量大於核心線程數時,任務會被放入到線程池的任務隊列中,當任務超過了最大隊列值時,判斷當前線程數量是否大於最大線程數,若是小於最大線程數則會新建立線程處理次任務,相反的狀況下就會執行拒絕策略,以下圖所示:
「視頻版」當線程池溢出以後,程序會奔潰嗎?面試突擊 007 期線程

模擬線程池溢出code

public static void main(String[] args) {
     ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS,
           new LinkedBlockingQueue<>(1),
           new ThreadPoolExecutor.AbortPolicy());
     for (int i = 0; i < 6; i++) {
         executor.execute(() -> {
             System.out.println(Thread.currentThread().getName());
         });
     }
 }
程序的執行結果以下:
pool-1-thread-2
pool-1-thread-2
pool-1-thread-1
pool-1-thread-3
pool-1-thread-4
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task org.example.App$$Lambda$1/1096979270@7cca494b rejected from java.util.concurrent.ThreadPoolExecutor@7ba4f24f[Running, pool size = 4, active threads = 4, queued tasks = 1, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at org.example.App.main(App.java:13)

從執行結果能夠看出,循環在執行第 6 次就拋出異常了,這是由於最大線程數爲 4,而隊列最大隻能存儲 1 個任務,因此在第 6 個任務過來的時候,線程池已經超負荷運行了,只能執行拒絕策略了,而咱們設置的拒絕策略是 AbortPolicy 因此會拋出異常。
自定義拒絕策略視頻

除了 Java 自帶的四種拒絕策略外,咱們還能夠自定義拒絕策略,代碼以下:blog

public static void main(String[] args) throws InterruptedException {
    ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(1), new RejectedExecutionHandler() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            // 添加業務處理代碼
            System.out.println("自定義拒絕策略");
        }
    });
    for (int i = 0; i < 7; i++) {
        executor.execute(() -> {
            System.out.println(Thread.currentThread().getName());
        });
    }
}

以上程序執行結果以下:
自定義拒絕策略
自定義拒絕策略
pool-1-thread-1
pool-1-thread-1
pool-1-thread-2
pool-1-thread-3
pool-1-thread-4
能夠看出自定義拒絕策略,只須要重寫 RejectedExecutionHandler 接口的 rejectedExecution 方法便可,能夠在此方法中添加本身的業務處理代碼。
小結接口

本文講了線程池任務新增時的執行流程,先判斷是否有空閒線程,若是的話直接執行任務,若是沒有的話再判斷任務隊列是不是否飽和,若是不飽和把任務存儲到隊列中,若是飽和須要判斷當前線程數是否大於最大線程數,若是小於則新增線程執行此任務,反之則執行拒絕策略。Java 提供了四種拒絕策略,你能夠經過重寫 RejectedExecutionHandler 接口來自定義拒絕策略。
更多執行細節和更多知識點說明,詳見本文的視頻部分。
【END】
近期熱文隊列

  • HashMap 爲何會致使 CPU 100%?面試突擊 006 期
  • 面試突擊 005 | Redis 是如何實現高可用的?它的實現方式有哪些?
  • 面試突擊 004 | 如何排查 Redis 中的慢查詢?視頻實戰篇
  • 面試突擊 003 | Redis 如何實現查詢附近的人?
  • 面試突擊 002 | Redis 是如何處理已過時元素的?
  • 面試突擊 001 | Redis 如何從海量數據中查詢出某一個 Key?
  • Java面試詳解(2020版):500+ 面試題和核心知識點詳解
    關注下方二維碼,訂閱更多精彩內容
    「視頻版」當線程池溢出以後,程序會奔潰嗎?面試突擊 007 期
相關文章
相關標籤/搜索