Springboot線程池異常處理的實現(xiàn)示例
在 Java 多線程編程中,線程池(ThreadPoolExecutor
)是一個常用的工具,用于管理線程的生命周期并提升應用程序的性能。然而,在使用線程池時,異常處理可能會被忽略,從而導致潛在的程序問題甚至崩潰。如果任務出現(xiàn)了異常,會發(fā)生什么呢?該怎么處理呢?怎么獲取到異常信息來解決異常?想要知道如何解決,就需要了解了解線程池提交任務的兩個方法execute
與submit
一.execute與submit
package demo1; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class Test2 { public static void main(String[] args) { //只有一個線程的線程池 ExecutorService threadPool = Executors.newFixedThreadPool(1); threadPool.execute(()->{ System.out.println("Execute: Current thread is " + Thread.currentThread().getName()); method_1(); }); Future<?> submit = threadPool.submit(() -> { System.out.println("Execute: Current thread is " + Thread.currentThread().getName()); method_1(); }); Future<?> submit2 = threadPool.submit(() -> { System.out.println("Execute: Current thread is " + Thread.currentThread().getName()); method_1(); }); } public static String method_1(){ System.out.println("starting....."); int i = 1/0; System.out.println("ending....."); return "ok"; } }
這里我execute
與submit
分別執(zhí)行兩個有異常的任務,同時打印了當前線程,以下是運行結果
可見execute
方法在遇到異常之后會拋出異常,并且線程池中的線程終止
submit
方法遇到異常不會拋出異常
特性 | execute | submit |
---|---|---|
方法定義 | void execute(Runnable command) | Future<?> submit(Runnable task)Future<T> submit(Callable<T> task) |
返回值 | 無返回值,不關心任務的執(zhí)行結果 | 返回 Future 對象,可用于獲取任務結果或狀態(tài) |
支持任務類型 | 僅支持 Runnable | 支持 Runnable 和 Callable |
異常處理 | 任務拋出的異常不會被捕獲,直接傳播到線程池的工作線程 | 任務拋出的異常會被封裝在 Future 中,需通過 Future.get() 獲取 |
使用場景 | 適用于無需獲取任務結果的場景 | 適用于需要獲取任務執(zhí)行結果或捕獲異常的場景 |
示例 | threadPool.execute(() -> method_1()); | Future<?> future = threadPool.submit(() -> method_1()); |
任務執(zhí)行方式 | 直接提交給線程池執(zhí)行 | 包裝為 FutureTask 后交由線程池執(zhí)行 |
線程池依賴 | 線程池的 execute 方法是基礎實現(xiàn) | submit 方法內(nèi)部調(diào)用 execute 執(zhí)行任務 |
異常傳播位置 | 通過默認的 UncaughtExceptionHandler 處理 | 通過 Future.get() 拋出異常 |
二.如何處理異常
2.1使用try-catch
這里不多贅述,這是最簡單明了的方法,直接用try-catch捕獲就行
2.2 使用 ThreadPoolExecutor 的 afterExecute 方法
ThreadPoolExecutor
提供了一個 afterExecute
鉤子方法,可以在任務完成后檢查是否有異常。
通過覆蓋此方法,可以捕獲所有任務中未被捕獲的異常。
package demo1; import java.io.IOException; import java.util.concurrent.*; public class Test3 { public static void main(String[] args) { ExecutorService threadPool = new /** * @author 方 */ ThreadPoolExecutor(1, 1, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()) { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); if (t != null) { System.out.println("Task threw an exception: " + t.getMessage()); } // 針對 Future 的異常處理 if (r instanceof Future<?>) { try { ((Future<?>) r).get(); // 調(diào)用 get 檢查任務是否拋出異常 } catch (Exception e) { System.out.println("Exception in Future: " + e.getCause()); } } } }; threadPool.execute(() -> { System.out.println("Execute: Current thread is " + Thread.currentThread().getName()); throw new RuntimeException("Test1 exception in execute"); }); threadPool.submit(() -> { System.out.println("Execute: Current thread is " + Thread.currentThread().getName()); throw new RuntimeException("Test2 exception in submit"); }); threadPool.submit(() -> { System.out.println("Execute: Current thread is " + Thread.currentThread().getName()); throw new RuntimeException("Test3 exception in submit"); }); threadPool.shutdown(); } }
運行結果:
可見execute
方法在遇到異常之后會拋出異常,并且線程池中的線程終止,submit
沒有拋出異常
但是他們兩個**都記錄了異常信息**
2.3設置 UncaughtExceptionHandler
package demo1; import java.io.IOException; import java.util.concurrent.*; public class Test3 { public static void main(String[] args) { //1.實現(xiàn)一個自己的線程池工廠 ThreadFactory factory = (Runnable r) -> { //創(chuàng)建一個線程 Thread t = new Thread(r); //給創(chuàng)建的線程設置UncaughtExceptionHandler對象 里面實現(xiàn)異常的默認邏輯 t.setDefaultUncaughtExceptionHandler((Thread thread1, Throwable e) -> { //出現(xiàn)異常 if (e != null){ System.out.println(Thread.currentThread().getName()+e.getMessage()); e.printStackTrace(); } }); return t; }; //2.創(chuàng)建一個自己定義的線程池,使用自己定義的線程工廠 ExecutorService threadPool = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(10), factory); threadPool.execute(() -> { System.out.println("Execute: Current thread is " + Thread.currentThread().getName()); throw new RuntimeException("Test1 exception in execute"); }); threadPool.submit(() -> { System.out.println("Execute: Current thread is " + Thread.currentThread().getName()); throw new RuntimeException("Test2 exception in submit"); }); threadPool.submit(() -> { System.out.println("Execute: Current thread is " + Thread.currentThread().getName()); throw new RuntimeException("Test3 exception in submit"); }); threadPool.shutdown(); } }
運行結果:
可見execute
方法在遇到異常之后會拋出異常,并且線程池中的線程終止,submit
沒有拋出異常
三.Springboot中的線程池異常處理
@Bean(MALLCHAT_EXECUTOR) @Primary public ThreadPoolTaskExecutor mallchatExecutor() { //spring的線程池 ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); //線程池優(yōu)雅停機的關鍵 executor.setWaitForTasksToCompleteOnShutdown(true); executor.setCorePoolSize(10); executor.setMaxPoolSize(10); executor.setQueueCapacity(200); executor.setThreadNamePrefix("mallchat-executor-"); //拒絕策略->滿了調(diào)用線程執(zhí)行,認為重要任務 executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); //自己就是一個線程工程 executor.setThreadFactory(new MyThreadFactory(executor)); executor.initialize(); return executor; }
package org.fth.mallchat.common.common.thread; import lombok.AllArgsConstructor; import java.util.concurrent.ThreadFactory; /** * @author 方 */ @AllArgsConstructor public class MyThreadFactory implements ThreadFactory { private static final MyUncaughtExceptionHandler MyUncaughtExceptionHandler = new MyUncaughtExceptionHandler(); private ThreadFactory original; @Override public Thread newThread(Runnable r) { //執(zhí)行Spring線程自己的創(chuàng)建邏輯 Thread thread = original.newThread(r); //我們自己額外的邏輯 thread.setUncaughtExceptionHandler(MyUncaughtExceptionHandler); return thread; } }
package org.fth.mallchat.common.common.thread; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author 方 */ public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { private static final Logger log = LoggerFactory.getLogger(MyUncaughtExceptionHandler.class); @Override public void uncaughtException(Thread t, Throwable e) { log.error("Exception in thread",e); } }
1. 線程池配置 (ThreadPoolTaskExecutor)
在 Spring Boot 中,ThreadPoolTaskExecutor
用于管理線程池的執(zhí)行,允許我們設置線程池的核心大小、最大線程數(shù)、隊列容量等。通過這種配置,我們可以控制線程池的資源使用情況,確保任務的執(zhí)行效率與可靠性。在你的代碼中,線程池的配置包括:
corePoolSize
和maxPoolSize
:指定線程池的最小和最大線程數(shù)。這里設置為 10,意味著線程池最多同時運行 10 個線程。queueCapacity
:指定任務隊列的容量。任務超過這個容量會被拒絕執(zhí)行,進入拒絕策略處理。setRejectedExecutionHandler
:設置任務隊列滿時的拒絕策略。在這個配置中,使用CallerRunsPolicy
,意味著如果隊列已滿,任務會直接在調(diào)用線程中執(zhí)行,而不是拋出異常。setWaitForTasksToCompleteOnShutdown(true)
:設置線程池在關閉時等待所有任務完成再退出,確保優(yōu)雅停機。
2. 自定義線程工廠 (ThreadFactory)
通過 ThreadFactory
,你可以自定義線程的創(chuàng)建過程。在你的代碼中,你為每個線程設置了一個異常處理器。這意味著,如果線程內(nèi)發(fā)生未捕獲的異常,這些異常會被專門的異常處理器捕獲并記錄,而不是導致線程崩潰或丟失異常信息。
- 自定義異常處理器:
Thread.setDefaultUncaughtExceptionHandler
會設置線程的默認異常處理器,確保在任何線程中出現(xiàn)未捕獲的異常時,異常都能被記錄。這個處理器的作用是將異常信息輸出到日志中,避免錯誤被忽略或?qū)е戮€程不可控。
3. 線程銷毀與異常
線程池的行為與異常處理相關:
- 如果線程發(fā)生未捕獲的異常,
UncaughtExceptionHandler
會記錄異常,但不會銷毀線程。線程池中的其他線程仍然會繼續(xù)工作。 - 線程池會自動重用空閑線程。即使某個線程發(fā)生異常,線程池仍然會創(chuàng)建新的線程來執(zhí)行其他任務,只要線程池的資源沒有完全用盡。
因此,線程池不會因單個線程的異常而銷毀整個線程池,它會繼續(xù)運行,并且通過異常處理機制記錄異常,確保系統(tǒng)的穩(wěn)定性。
現(xiàn)未捕獲的異常時,異常都能被記錄。這個處理器的作用是將異常信息輸出到日志中,避免錯誤被忽略或?qū)е戮€程不可控。
不過sumbit
還是必須要get
才能拿到異常信息,我們還是可以通過重寫ThreadPoolExecutor
的 afterExecute
方法 不過這樣的話就有點麻煩
到此這篇關于Springboot線程池異常處理的實現(xiàn)示例的文章就介紹到這了,更多相關Springboot線程池異常處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
- Springboot應用中線程池配置詳細教程(最新2021版)
- SpringBoot+slf4j線程池全鏈路調(diào)用日志跟蹤問題及解決思路(二)
- springboot使用線程池(ThreadPoolTaskExecutor)示例
- Springboot線程池并發(fā)處理數(shù)據(jù)優(yōu)化方式
- springboot創(chuàng)建線程池的兩種方式小結
- Springboot?配置線程池創(chuàng)建線程及配置?@Async?異步操作線程池詳解
- SpringBoot實現(xiàn)自定義線程池的方法
- SpringBoot自定義線程池,執(zhí)行定時任務方式
- SpringBoot?全局線程池配置及應用小結
相關文章
基于Java將Excel科學計數(shù)法解析成數(shù)字
這篇文章主要介紹了基于Java將Excel科學計數(shù)法解析成數(shù)字,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-09-09Java VisualVM監(jiān)控遠程JVM(詳解)
下面小編就為大家?guī)硪黄狫ava VisualVM監(jiān)控遠程JVM(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10深入了解HttpClient的ResponseHandler接口
這篇文章主要為大家介紹了深入了解HttpClient的ResponseHandler接口,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-10-10