Java線程池內(nèi)部任務(wù)出異常后發(fā)現(xiàn)異常的3種方法
前言
在使用 Java 線程池(ThreadPoolExecutor) 進(jìn)行并發(fā)任務(wù)執(zhí)行時(shí),默認(rèn)情況下 線程池不會(huì)直接報(bào)告某個(gè)線程發(fā)生了異常,這可能會(huì)導(dǎo)致問題難以排查。例如,任務(wù)拋出異常后,線程池可能會(huì)靜默失敗,甚至導(dǎo)致線程丟失。
那么,當(dāng)線程池內(nèi)部的任務(wù)發(fā)生異常時(shí),我們?nèi)绾尾东@它,并知道具體是哪個(gè)線程出了問題呢?本文將介紹 3 種方法 來解決這個(gè)問題。
1. 自定義 ThreadFactory 并設(shè)置異常處理器
Java 提供了 UncaughtExceptionHandler,它允許我們?cè)诰€程發(fā)生未捕獲異常時(shí)執(zhí)行自定義邏輯。我們可以自定義 ThreadFactory,為每個(gè)線程設(shè)置異常處理器,確保當(dāng)任務(wù)崩潰時(shí)能夠記錄具體的線程信息。
實(shí)現(xiàn)步驟
- 自定義 ThreadFactory
- 為線程設(shè)置 UncaughtExceptionHandler
- 在異常發(fā)生時(shí)打印錯(cuò)誤信息
示例代碼
import java.util.concurrent.*;
public class CustomThreadFactoryDemo {
public static void main(String[] args) {
ExecutorService executor = new ThreadPoolExecutor(
2, 4, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>(10),
new CustomThreadFactory()
);
executor.execute(() -> {
throw new RuntimeException("任務(wù)異常,測(cè)試線程異常處理!");
});
executor.shutdown();
}
// 自定義 ThreadFactory
static class CustomThreadFactory implements ThreadFactory {
private final ThreadFactory defaultFactory = Executors.defaultThreadFactory();
@Override
public Thread newThread(Runnable r) {
Thread thread = defaultFactory.newThread(r);
thread.setUncaughtExceptionHandler((t, e) -> {
System.err.println("線程 " + t.getName() + " 出現(xiàn)異常: " + e.getMessage());
});
return thread;
}
}
}
輸出
線程 pool-1-thread-1 出現(xiàn)異常: 任務(wù)異常,測(cè)試線程異常處理!
通過 UncaughtExceptionHandler,我們能夠捕獲到具體的 線程名稱 和 異常信息,方便排查問題。
2. 使用 Future 捕獲異常
如果你是通過 submit() 提交任務(wù),而不是 execute(),那么可以使用 Future 對(duì)象來捕獲異常。
實(shí)現(xiàn)步驟
- 使用
submit()方法提交任務(wù)(execute()不會(huì)返回Future)。 - 通過
Future.get()獲取結(jié)果,如果任務(wù)異常,會(huì)拋出 ExecutionException。 - 在
catch語句中提取具體的 線程信息 和 異常信息。
示例代碼
import java.util.concurrent.*;
public class FutureExceptionHandlingDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
Future<?> future = executor.submit(() -> {
throw new RuntimeException("任務(wù)執(zhí)行失??!");
});
try {
future.get(); // get() 方法會(huì)拋出異常
} catch (InterruptedException | ExecutionException e) {
System.err.println("線程 " + Thread.currentThread().getName() + " 捕獲到任務(wù)異常: " + e.getCause().getMessage());
}
executor.shutdown();
}
}
輸出
線程 main 捕獲到任務(wù)異常: 任務(wù)執(zhí)行失敗!
優(yōu)點(diǎn)
submit()返回Future,不會(huì)導(dǎo)致線程池線程終止,可以繼續(xù)執(zhí)行其他任務(wù)。Future.get()同步等待任務(wù)完成,適用于需要獲取結(jié)果的場(chǎng)景。
3. 任務(wù)內(nèi)部手動(dòng)捕獲異常并記錄
如果任務(wù)執(zhí)行過程中發(fā)生異常,我們可以 手動(dòng) try-catch 捕獲,然后記錄 線程信息,避免任務(wù)異常導(dǎo)致線程退出。
實(shí)現(xiàn)步驟
- 在
run()方法內(nèi)部使用 try-catch 捕獲異常。 - 使用
Thread.currentThread().getName()記錄當(dāng)前線程的異常信息。
示例代碼
import java.util.concurrent.*;
public class TryCatchExceptionHandlingDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(() -> {
try {
throw new RuntimeException("任務(wù)崩潰!");
} catch (Exception e) {
System.err.println("線程 " + Thread.currentThread().getName() + " 發(fā)生異常: " + e.getMessage());
}
});
executor.shutdown();
}
}
輸出
線程 pool-1-thread-1 發(fā)生異常: 任務(wù)崩潰!
優(yōu)點(diǎn)
- 適用于業(yè)務(wù)邏輯層,任務(wù)內(nèi)部可以根據(jù)異常類型做不同處理。
- 不會(huì)影響線程池的正常運(yùn)行,任務(wù)失敗后線程仍可復(fù)用。
總結(jié)
| 方案 | 適用場(chǎng)景 | 優(yōu)勢(shì) | 缺點(diǎn) |
|---|---|---|---|
| 自定義 ThreadFactory + UncaughtExceptionHandler | 適用于 execute() 提交任務(wù)的情況 | 可以直接捕獲線程異常,自動(dòng)記錄線程信息 | 僅適用于 execute(),不能用于 submit() |
| 使用 Future 處理異常 | 適用于 submit() 提交任務(wù)的情況 | get() 方法可以捕獲異常,不影響線程池運(yùn)行 | 需要手動(dòng) get(),否則無法捕獲異常 |
| 任務(wù)內(nèi)部手動(dòng) try-catch | 適用于所有情況 | 任務(wù)內(nèi)部可以靈活處理異常,保證線程池穩(wěn)定性 | 需要開發(fā)者手動(dòng)捕獲,可能會(huì)遺漏異常 |
在實(shí)際應(yīng)用中:
- 如果你使用 execute() 提交任務(wù),推薦 自定義 ThreadFactory 設(shè)置異常處理器。
- 如果你使用 submit() 提交任務(wù),建議 使用 Future 捕獲異常。
- 如果你想完全控制異常,可以 在任務(wù)內(nèi)部 try-catch 處理。
到此這篇關(guān)于Java線程池內(nèi)部任務(wù)出異常后發(fā)現(xiàn)異常的3種方法的文章就介紹到這了,更多相關(guān)Java線程池內(nèi)部任務(wù)出異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java日常練習(xí)題,每天進(jìn)步一點(diǎn)點(diǎn)(17)
下面小編就為大家?guī)硪黄狫ava基礎(chǔ)的幾道練習(xí)題(分享)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧,希望可以幫到你2021-07-07
SpringBoot參數(shù)校驗(yàn)之@Valid的使用詳解
這篇文章主要通過示例為大家詳細(xì)介紹一下介紹了SpringBoot參數(shù)校驗(yàn)中@Valid的使用方法,文中的示例代碼講解詳細(xì),需要的可以參考一下2022-06-06
詳解Java使用Jsch與sftp服務(wù)器實(shí)現(xiàn)ssh免密登錄
這篇文章主要介紹了詳解Java使用Jsch與sftp服務(wù)器實(shí)現(xiàn)ssh免密登錄,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
Java手機(jī)號(hào)最新校驗(yàn)規(guī)則
在Java中,進(jìn)行手機(jī)號(hào)校驗(yàn)通常使用正則表達(dá)式(Regex)來匹配手機(jī)號(hào)的格式,以下是一個(gè)基于當(dāng)前(截至2024年)中國(guó)手機(jī)號(hào)規(guī)則的校驗(yàn)方法,感興趣的朋友跟隨小編一起看看吧2024-05-05
Java 根據(jù)網(wǎng)址查詢DNS/IP地址的方法
這篇文章主要介紹了Java 根據(jù)網(wǎng)址查詢DNS/IP地址的方法,具體實(shí)現(xiàn)代碼,大家參考下本文2017-12-12
SpringBoot2整合Redis實(shí)現(xiàn)讀寫操作
Redis,對(duì)于大家來說應(yīng)該不陌生,是經(jīng)常使用的開發(fā)技術(shù)之一。本文將結(jié)合實(shí)例代碼,介紹SpringBoot2整合Redis實(shí)現(xiàn)讀寫操作,感興趣的小伙伴們可以參考一下2021-07-07
spring自定義注解實(shí)現(xiàn)攔截器的實(shí)現(xiàn)方法
本篇文章主要介紹了spring自定義注解實(shí)現(xiàn)攔截器的實(shí)現(xiàn)方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-08-08

