Java線(xiàn)程池內(nèi)部任務(wù)出異常后發(fā)現(xiàn)異常的3種方法
前言
在使用 Java 線(xiàn)程池(ThreadPoolExecutor) 進(jìn)行并發(fā)任務(wù)執(zhí)行時(shí),默認(rèn)情況下 線(xiàn)程池不會(huì)直接報(bào)告某個(gè)線(xiàn)程發(fā)生了異常,這可能會(huì)導(dǎo)致問(wèn)題難以排查。例如,任務(wù)拋出異常后,線(xiàn)程池可能會(huì)靜默失敗,甚至導(dǎo)致線(xiàn)程丟失。
那么,當(dāng)線(xiàn)程池內(nèi)部的任務(wù)發(fā)生異常時(shí),我們?nèi)绾尾东@它,并知道具體是哪個(gè)線(xiàn)程出了問(wèn)題呢?本文將介紹 3 種方法 來(lái)解決這個(gè)問(wèn)題。
1. 自定義 ThreadFactory 并設(shè)置異常處理器
Java 提供了 UncaughtExceptionHandler,它允許我們?cè)诰€(xiàn)程發(fā)生未捕獲異常時(shí)執(zhí)行自定義邏輯。我們可以自定義 ThreadFactory,為每個(gè)線(xiàn)程設(shè)置異常處理器,確保當(dāng)任務(wù)崩潰時(shí)能夠記錄具體的線(xiàn)程信息。
實(shí)現(xiàn)步驟
- 自定義 ThreadFactory
- 為線(xiàn)程設(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è)試線(xiàn)程異常處理!");
});
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("線(xiàn)程 " + t.getName() + " 出現(xiàn)異常: " + e.getMessage());
});
return thread;
}
}
}
輸出
線(xiàn)程 pool-1-thread-1 出現(xiàn)異常: 任務(wù)異常,測(cè)試線(xiàn)程異常處理!
通過(guò) UncaughtExceptionHandler,我們能夠捕獲到具體的 線(xiàn)程名稱(chēng) 和 異常信息,方便排查問(wèn)題。
2. 使用 Future 捕獲異常
如果你是通過(guò) submit() 提交任務(wù),而不是 execute(),那么可以使用 Future 對(duì)象來(lái)捕獲異常。
實(shí)現(xiàn)步驟
- 使用
submit()方法提交任務(wù)(execute()不會(huì)返回Future)。 - 通過(guò)
Future.get()獲取結(jié)果,如果任務(wù)異常,會(huì)拋出 ExecutionException。 - 在
catch語(yǔ)句中提取具體的 線(xiàn)程信息 和 異常信息。
示例代碼
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("線(xiàn)程 " + Thread.currentThread().getName() + " 捕獲到任務(wù)異常: " + e.getCause().getMessage());
}
executor.shutdown();
}
}
輸出
線(xiàn)程 main 捕獲到任務(wù)異常: 任務(wù)執(zhí)行失??!
優(yōu)點(diǎn)
submit()返回Future,不會(huì)導(dǎo)致線(xiàn)程池線(xiàn)程終止,可以繼續(xù)執(zhí)行其他任務(wù)。Future.get()同步等待任務(wù)完成,適用于需要獲取結(jié)果的場(chǎng)景。
3. 任務(wù)內(nèi)部手動(dòng)捕獲異常并記錄
如果任務(wù)執(zhí)行過(guò)程中發(fā)生異常,我們可以 手動(dòng) try-catch 捕獲,然后記錄 線(xiàn)程信息,避免任務(wù)異常導(dǎo)致線(xiàn)程退出。
實(shí)現(xiàn)步驟
- 在
run()方法內(nèi)部使用 try-catch 捕獲異常。 - 使用
Thread.currentThread().getName()記錄當(dāng)前線(xiàn)程的異常信息。
示例代碼
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("線(xiàn)程 " + Thread.currentThread().getName() + " 發(fā)生異常: " + e.getMessage());
}
});
executor.shutdown();
}
}
輸出
線(xiàn)程 pool-1-thread-1 發(fā)生異常: 任務(wù)崩潰!
優(yōu)點(diǎn)
- 適用于業(yè)務(wù)邏輯層,任務(wù)內(nèi)部可以根據(jù)異常類(lèi)型做不同處理。
- 不會(huì)影響線(xiàn)程池的正常運(yùn)行,任務(wù)失敗后線(xiàn)程仍可復(fù)用。
總結(jié)
| 方案 | 適用場(chǎng)景 | 優(yōu)勢(shì) | 缺點(diǎn) |
|---|---|---|---|
| 自定義 ThreadFactory + UncaughtExceptionHandler | 適用于 execute() 提交任務(wù)的情況 | 可以直接捕獲線(xiàn)程異常,自動(dòng)記錄線(xiàn)程信息 | 僅適用于 execute(),不能用于 submit() |
| 使用 Future 處理異常 | 適用于 submit() 提交任務(wù)的情況 | get() 方法可以捕獲異常,不影響線(xiàn)程池運(yùn)行 | 需要手動(dòng) get(),否則無(wú)法捕獲異常 |
| 任務(wù)內(nèi)部手動(dòng) try-catch | 適用于所有情況 | 任務(wù)內(nèi)部可以靈活處理異常,保證線(xiàn)程池穩(wěn)定性 | 需要開(kāi)發(fā)者手動(dòng)捕獲,可能會(huì)遺漏異常 |
在實(shí)際應(yīng)用中:
- 如果你使用 execute() 提交任務(wù),推薦 自定義 ThreadFactory 設(shè)置異常處理器。
- 如果你使用 submit() 提交任務(wù),建議 使用 Future 捕獲異常。
- 如果你想完全控制異常,可以 在任務(wù)內(nèi)部 try-catch 處理。
到此這篇關(guān)于Java線(xiàn)程池內(nèi)部任務(wù)出異常后發(fā)現(xiàn)異常的3種方法的文章就介紹到這了,更多相關(guān)Java線(xiàn)程池內(nèi)部任務(wù)出異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot實(shí)現(xiàn)垂直分片的六種策略
隨著業(yè)務(wù)規(guī)模的不斷擴(kuò)大,單一數(shù)據(jù)庫(kù)架構(gòu)往往難以滿(mǎn)足日益增長(zhǎng)的數(shù)據(jù)量和訪(fǎng)問(wèn)壓力,作為解決方案之一,垂直分片通過(guò)將不同業(yè)務(wù)模塊的數(shù)據(jù)分散到不同的數(shù)據(jù)庫(kù)或?qū)嵗?本文將介紹在SpringBoot環(huán)境下實(shí)現(xiàn)垂直分片的六種策略,需要的朋友可以參考下2025-06-06
Java對(duì)類(lèi)私有變量的暴力反射技術(shù)講解
今天小編就為大家分享一篇關(guān)于Java對(duì)類(lèi)私有變量的暴力反射技術(shù)講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-03-03
SpringBoot中使用@Async實(shí)現(xiàn)異步任務(wù)調(diào)用詳解
這篇文章主要介紹了SpringBoot中使用@Async實(shí)現(xiàn)異步任務(wù)調(diào)用詳解,一個(gè)可以無(wú)需等待被調(diào)用函數(shù)的返回值就讓操作繼續(xù)進(jìn)行的方法(來(lái)自百度百科),即程序在順序執(zhí)行時(shí),不等待異步調(diào)用的語(yǔ)句返回結(jié)果就執(zhí)行后面的程序,需要的朋友可以參考下2023-12-12
Java 實(shí)戰(zhàn)項(xiàng)目錘煉之仿天貓網(wǎng)上商城的實(shí)現(xiàn)流程
讀萬(wàn)卷書(shū)不如行萬(wàn)里路,只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+jsp+servlet+mysql+ajax實(shí)現(xiàn)一個(gè)仿天貓網(wǎng)上商城項(xiàng)目,大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2021-11-11
SpringMVC整合,出現(xiàn)注解沒(méi)有起作用的情況處理
這篇文章主要介紹了SpringMVC整合,出現(xiàn)注解沒(méi)有起作用的情況及處理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。2023-05-05
Windows同時(shí)安裝兩個(gè)版本JDK并實(shí)現(xiàn)動(dòng)態(tài)切換JAVA8或JAVA11的方法
這篇文章主要給大家介紹了關(guān)于Windows同時(shí)安裝兩個(gè)版本JDK并實(shí)現(xiàn)動(dòng)態(tài)切換JAVA8或JAVA11的相關(guān)資料,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-11-11
Android開(kāi)發(fā)在輪播圖片上加入點(diǎn)擊事件的方法
這篇文章主要介紹了Android開(kāi)發(fā)在輪播圖片上加入點(diǎn)擊事件的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-11-11
springmvc處理模型數(shù)據(jù)Map過(guò)程解析
這篇文章主要介紹了springmvc處理模型數(shù)據(jù)Map過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01

