Springboot線程池異常處理的實(shí)現(xiàn)示例
在 Java 多線程編程中,線程池(ThreadPoolExecutor)是一個常用的工具,用于管理線程的生命周期并提升應(yīng)用程序的性能。然而,在使用線程池時,異常處理可能會被忽略,從而導(dǎo)致潛在的程序問題甚至崩潰。如果任務(wù)出現(xiàn)了異常,會發(fā)生什么呢?該怎么處理呢?怎么獲取到異常信息來解決異常?想要知道如何解決,就需要了解了解線程池提交任務(wù)的兩個方法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í)行兩個有異常的任務(wù),同時打印了當(dāng)前線程,以下是運(yùn)行結(jié)果

可見execute方法在遇到異常之后會拋出異常,并且線程池中的線程終止
submit方法遇到異常不會拋出異常
| 特性 | execute | submit |
|---|---|---|
| 方法定義 | void execute(Runnable command) | Future<?> submit(Runnable task)Future<T> submit(Callable<T> task) |
| 返回值 | 無返回值,不關(guān)心任務(wù)的執(zhí)行結(jié)果 | 返回 Future 對象,可用于獲取任務(wù)結(jié)果或狀態(tài) |
| 支持任務(wù)類型 | 僅支持 Runnable | 支持 Runnable 和 Callable |
| 異常處理 | 任務(wù)拋出的異常不會被捕獲,直接傳播到線程池的工作線程 | 任務(wù)拋出的異常會被封裝在 Future 中,需通過 Future.get() 獲取 |
| 使用場景 | 適用于無需獲取任務(wù)結(jié)果的場景 | 適用于需要獲取任務(wù)執(zhí)行結(jié)果或捕獲異常的場景 |
| 示例 | threadPool.execute(() -> method_1()); | Future<?> future = threadPool.submit(() -> method_1()); |
| 任務(wù)執(zhí)行方式 | 直接提交給線程池執(zhí)行 | 包裝為 FutureTask 后交由線程池執(zhí)行 |
| 線程池依賴 | 線程池的 execute 方法是基礎(chǔ)實(shí)現(xiàn) | submit 方法內(nèi)部調(diào)用 execute 執(zhí)行任務(wù) |
| 異常傳播位置 | 通過默認(rèn)的 UncaughtExceptionHandler 處理 | 通過 Future.get() 拋出異常 |
二.如何處理異常
2.1使用try-catch
這里不多贅述,這是最簡單明了的方法,直接用try-catch捕獲就行
2.2 使用 ThreadPoolExecutor 的 afterExecute 方法
ThreadPoolExecutor 提供了一個 afterExecute 鉤子方法,可以在任務(wù)完成后檢查是否有異常。
通過覆蓋此方法,可以捕獲所有任務(wù)中未被捕獲的異常。
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 檢查任務(wù)是否拋出異常
} 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();
}
}
運(yùn)行結(jié)果:

可見execute方法在遇到異常之后會拋出異常,并且線程池中的線程終止,submit沒有拋出異常
但是他們兩個**都記錄了異常信息**
2.3設(shè)置 UncaughtExceptionHandler
package demo1;
import java.io.IOException;
import java.util.concurrent.*;
public class Test3 {
public static void main(String[] args) {
//1.實(shí)現(xiàn)一個自己的線程池工廠
ThreadFactory factory = (Runnable r) -> {
//創(chuàng)建一個線程
Thread t = new Thread(r);
//給創(chuàng)建的線程設(shè)置UncaughtExceptionHandler對象 里面實(shí)現(xiàn)異常的默認(rè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();
}
}
運(yùn)行結(jié)果:

可見execute方法在遇到異常之后會拋出異常,并且線程池中的線程終止,submit沒有拋出異常
三.Springboot中的線程池異常處理
@Bean(MALLCHAT_EXECUTOR)
@Primary
public ThreadPoolTaskExecutor mallchatExecutor() {
//spring的線程池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
//線程池優(yōu)雅停機(jī)的關(guān)鍵
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setCorePoolSize(10);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("mallchat-executor-");
//拒絕策略->滿了調(diào)用線程執(zhí)行,認(rèn)為重要任務(wù)
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è)置線程池的核心大小、最大線程數(shù)、隊(duì)列容量等。通過這種配置,我們可以控制線程池的資源使用情況,確保任務(wù)的執(zhí)行效率與可靠性。在你的代碼中,線程池的配置包括:
corePoolSize和maxPoolSize:指定線程池的最小和最大線程數(shù)。這里設(shè)置為 10,意味著線程池最多同時運(yùn)行 10 個線程。queueCapacity:指定任務(wù)隊(duì)列的容量。任務(wù)超過這個容量會被拒絕執(zhí)行,進(jìn)入拒絕策略處理。setRejectedExecutionHandler:設(shè)置任務(wù)隊(duì)列滿時的拒絕策略。在這個配置中,使用CallerRunsPolicy,意味著如果隊(duì)列已滿,任務(wù)會直接在調(diào)用線程中執(zhí)行,而不是拋出異常。setWaitForTasksToCompleteOnShutdown(true):設(shè)置線程池在關(guān)閉時等待所有任務(wù)完成再退出,確保優(yōu)雅停機(jī)。
2. 自定義線程工廠 (ThreadFactory)
通過 ThreadFactory,你可以自定義線程的創(chuàng)建過程。在你的代碼中,你為每個線程設(shè)置了一個異常處理器。這意味著,如果線程內(nèi)發(fā)生未捕獲的異常,這些異常會被專門的異常處理器捕獲并記錄,而不是導(dǎo)致線程崩潰或丟失異常信息。
- 自定義異常處理器:
Thread.setDefaultUncaughtExceptionHandler會設(shè)置線程的默認(rèn)異常處理器,確保在任何線程中出現(xiàn)未捕獲的異常時,異常都能被記錄。這個處理器的作用是將異常信息輸出到日志中,避免錯誤被忽略或?qū)е戮€程不可控。
3. 線程銷毀與異常
線程池的行為與異常處理相關(guān):
- 如果線程發(fā)生未捕獲的異常,
UncaughtExceptionHandler會記錄異常,但不會銷毀線程。線程池中的其他線程仍然會繼續(xù)工作。 - 線程池會自動重用空閑線程。即使某個線程發(fā)生異常,線程池仍然會創(chuàng)建新的線程來執(zhí)行其他任務(wù),只要線程池的資源沒有完全用盡。
因此,線程池不會因單個線程的異常而銷毀整個線程池,它會繼續(xù)運(yùn)行,并且通過異常處理機(jī)制記錄異常,確保系統(tǒng)的穩(wěn)定性。
現(xiàn)未捕獲的異常時,異常都能被記錄。這個處理器的作用是將異常信息輸出到日志中,避免錯誤被忽略或?qū)е戮€程不可控。
不過sumbit還是必須要get才能拿到異常信息,我們還是可以通過重寫ThreadPoolExecutor 的 afterExecute 方法 不過這樣的話就有點(diǎn)麻煩
到此這篇關(guān)于Springboot線程池異常處理的實(shí)現(xiàn)示例的文章就介紹到這了,更多相關(guān)Springboot線程池異常處理內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot應(yīng)用中線程池配置詳細(xì)教程(最新2021版)
- SpringBoot+slf4j線程池全鏈路調(diào)用日志跟蹤問題及解決思路(二)
- springboot使用線程池(ThreadPoolTaskExecutor)示例
- Springboot線程池并發(fā)處理數(shù)據(jù)優(yōu)化方式
- springboot創(chuàng)建線程池的兩種方式小結(jié)
- Springboot?配置線程池創(chuàng)建線程及配置?@Async?異步操作線程池詳解
- SpringBoot實(shí)現(xiàn)自定義線程池的方法
- SpringBoot自定義線程池,執(zhí)行定時任務(wù)方式
- SpringBoot?全局線程池配置及應(yīng)用小結(jié)
相關(guān)文章
java實(shí)現(xiàn)文件上傳下載至ftp服務(wù)器
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)文件上傳下載至ftp服務(wù)器的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
基于Java將Excel科學(xué)計(jì)數(shù)法解析成數(shù)字
這篇文章主要介紹了基于Java將Excel科學(xué)計(jì)數(shù)法解析成數(shù)字,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
Java VisualVM監(jiān)控遠(yuǎn)程JVM(詳解)
下面小編就為大家?guī)硪黄狫ava VisualVM監(jiān)控遠(yuǎn)程JVM(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10
深入了解HttpClient的ResponseHandler接口
這篇文章主要為大家介紹了深入了解HttpClient的ResponseHandler接口,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10

