本文的面試題是,當線程池的任務溢出以後,程序會奔潰嗎?
這個問題問的是關於線程池的任務數超過線程池的承載能力以後,會出現什麼狀況?
那麼,咱們本文就手擼模擬一個線程池溢出的狀況,來看程序的執行狀況。
涉及知識點java
視頻內容(由於視頻比較大,分紅了兩個視頻來展現 ):面試
圖文面試答案ide
當線程池的任務溢出以後,程序並不會奔潰,這時候會觸發線程池的拒絕策略,Java 自帶的拒絕策略有四種:
AbortPolicy:終止策略,線程池終止執行,並直接拋出異常,Java 默認此拒絕策略;
CallerRunsPolicy:把任務交給當前線程執行(原本是線程池本身要執行的,結果處理不過來就交給當前的主線程去處理);
DiscardPolicy:忽略此任務(最新的任務);
DiscardOldestPolicy:忽略最先的任務(最久的任務)。
拒絕策略的執行流程比較繞,這是由於線程池有三個重要的參數:核心線程數(corePoolSize)、最大線程數(maximumPoolSize)、線程池的任務隊列(BlockingQueue),大部分搞不清楚核心線程數和最大線程數有什麼區別?
核心線程數是指在正常狀況下線程池內的線程數量;而最大線程數指的是當線程池的任務隊列存儲超過最大值以後,能夠建立最多的線程數量。
當任務比較少的時候,線程數量會根據設置的超時時間,迴歸線程的數量爲核心線程數量,這個時候最大線程數就暫時沒用了(沒有發揮的餘地了)。
拒絕策略的執行流程是:當提交的任務數量大於核心線程數時,任務會被放入到線程池的任務隊列中,當任務超過了最大隊列值時,判斷當前線程數量是否大於最大線程數,若是小於最大線程數則會新建立線程處理次任務,相反的狀況下就會執行拒絕策略,以下圖所示:
線程
模擬線程池溢出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】
近期熱文隊列