「视频版」当线程池溢出之后,程序会奔溃吗?面试突击 007 期
哈嘍,大家好,我是老王,歡迎來到第 7 期的 Java 面試突擊。
本文的面試題是,當線程池的任務溢出之后,程序會奔潰嗎?
這個問題問的是關于線程池的任務數超過線程池的承載能力之后,會出現什么情況?
那么,我們本文就手擼模擬一個線程池溢出的情況,來看程序的執行情況。
涉及知識點
核心線程數和最大線程數有什么區別?
如何模擬線程池溢出?
拒絕策略的執行流程是什么?
什么是線程池的拒絕策略?
Java 自帶的拒絕策略有哪些?
如何自定義拒絕策略?
視頻面試答案
視頻內容(因為視頻比較大,分成了兩個視頻來展示?):
圖文面試答案
當線程池的任務溢出之后,程序并不會奔潰,這時候會觸發線程池的拒絕策略,Java 自帶的拒絕策略有四種:
AbortPolicy:終止策略,線程池終止執行,并直接拋出異常,Java 默認此拒絕策略;
CallerRunsPolicy:把任務交給當前線程執行(本來是線程池自己要執行的,結果處理不過來就交給當前的主線程去處理);
DiscardPolicy:忽略此任務(最新的任務);
DiscardOldestPolicy:忽略最早的任務(最久的任務)。
拒絕策略的執行流程比較繞,這是因為線程池有三個重要的參數:核心線程數(corePoolSize)、最大線程數(maximumPoolSize)、線程池的任務隊列(BlockingQueue),大部分搞不清楚核心線程數和最大線程數有什么區別?
核心線程數是指在正常情況下線程池內的線程數量;而最大線程數指的是當線程池的任務隊列存儲超過最大值之后,可以創建最多的線程數量。
當任務比較少的時候,線程數量會根據設置的超時時間,回歸線程的數量為核心線程數量,這個時候最大線程數就暫時沒用了(沒有發揮的余地了)。
拒絕策略的執行流程是:當提交的任務數量大于核心線程數時,任務會被放入到線程池的任務隊列中,當任務超過了最大隊列值時,判斷當前線程數量是否大于最大線程數,如果小于最大線程數則會新創建線程處理次任務,相反的情況下就會執行拒絕策略,如下圖所示:
模擬線程池溢出
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 自帶的四種拒絕策略外,我們還可以自定義拒絕策略,代碼如下:
public static void main(String[] args) throws InterruptedException {ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 4, 10, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1), new RejectedExecutionHandler() {@Overridepublic 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 期的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring Boot (八)MyBat
- 下一篇: Java 200+ 面试题补充③ Dub