基于Java子線程中的異常處理方法(通用)
在普通的單線程程序中,捕獲異常只需要通過(guò)try ... catch ... finally ...代碼塊就可以了。那么,在并發(fā)情況下,比如在父線程中啟動(dòng)了子線程,如何在父線程中捕獲來(lái)自子線程的異常,從而進(jìn)行相應(yīng)的處理呢?
常見錯(cuò)誤
也許有人會(huì)覺得,很簡(jiǎn)單嘛,直接在父線程啟動(dòng)子線程的地方try ... catch一把就可以了,其實(shí)這是不對(duì)的。
原因分析
讓我們回憶一下Runnable接口的run方法的完整簽名,因?yàn)闆]有標(biāo)識(shí)throws語(yǔ)句,所以方法是不會(huì)拋出checked異常的。至于RuntimeException這樣的unchecked異常,由于新線程由JVM進(jìn)行調(diào)度執(zhí)行,如果發(fā)生了異常,也不會(huì)通知到父線程。
public abstract void run()
解決辦法
那么,如何在父線程中捕獲來(lái)自子線程的異常呢?樓主想到了3種常用方法,分享給大家。
方法一:子線程中try... catch...
最簡(jiǎn)單有效的辦法,就是在子線程的方法中,把可能發(fā)生異常的地方,用try ... catch ... 語(yǔ)句包起來(lái)。子線程代碼:
public class ChildThread implements Runnable {
public void run() {
doSomething1();
try {
// 可能發(fā)生異常的方法
exceptionMethod();
} catch (Exception e) {
// 處理異常
System.out.println(String.format("handle exception in child thread. %s", e));
}
doSomething2();
}
}
方法二:為線程設(shè)置異常處理器UncaughtExceptionHandler
為線程設(shè)置異常處理器。具體做法可以是以下幾種:
(1)Thread.setUncaughtExceptionHandler設(shè)置當(dāng)前線程的異常處理器
(2)Thread.setDefaultUncaughtExceptionHandler為整個(gè)程序設(shè)置默認(rèn)的異常處理器如果當(dāng)前線程有異常處理器(默認(rèn)沒有),則優(yōu)先使用該UncaughtExceptionHandler類;否則,如果當(dāng)前線程所屬的線程組有異常處理器,則使用線程組的ExceptionHandler;否則,使用全局默認(rèn)的DefaultUncaughtExceptionHandler;如果都沒有的話,子線程就會(huì)退出。
注意:子線程中發(fā)生了異常,如果沒有任何類來(lái)接手處理的話,是會(huì)直接退出的,而不會(huì)留下打印任何日志。所以,如果什么都不做的話,是會(huì)出現(xiàn)子線程任務(wù)既沒執(zhí)行,也沒有任何日志提示的“詭異”現(xiàn)象的。
設(shè)置當(dāng)前線程的異常處理器:
public class ChildThread implements Runnable {
private static ChildThreadExceptionHandler exceptionHandler;
static {
exceptionHandler = new ChildThreadExceptionHandler();
}
public void run() {
Thread.currentThread().setUncaughtExceptionHandler(exceptionHandler);
System.out.println("do something 1");
exceptionMethod();
System.out.println("do something 2");
}
public static class ChildThreadExceptionHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.println(String.format("handle exception in child thread. %s", e));
}
}
}
或者,設(shè)置所有線程的默認(rèn)異常處理器
public class ChildThread implements Runnable {
private static ChildThreadExceptionHandler exceptionHandler;
static {
exceptionHandler = new ChildThreadExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
}
public void run() {
System.out.println("do something 1");
exceptionMethod();
System.out.println("do something 2");
}
private void exceptionMethod() {
throw new RuntimeException("ChildThread exception");
}
public static class ChildThreadExceptionHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
System.out.println(String.format("handle exception in child thread. %s", e));
}
}
}
命令行輸出:do something 1
handle exception in child thread. java.lang.RuntimeException: ChildThread exception
方法三,通過(guò)Future的get方法捕獲異常
使用線程池提交一個(gè)能獲取到返回信息的方法,也就是ExecutorService.submit(Callable)在submit之后可以獲得一個(gè)線程執(zhí)行結(jié)果的Future對(duì)象,而如果子線程中發(fā)生了異常,通過(guò)future.get()獲取返回值時(shí),可以捕獲到ExecutionException異常,從而知道子線程中發(fā)生了異常。
子線程代碼:
public class ChildThread implements Callable {
public Object call() throws Exception {
System.out.println("do something 1");
exceptionMethod();
System.out.println("do something 2");
return null;
}
private void exceptionMethod() {
throw new RuntimeException("ChildThread1 exception");
}
}
父線程代碼:
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(8);
Future future = executorService.submit(new ChildThread());
try {
future.get();
} catch (InterruptedException e) {
System.out.println(String.format("handle exception in child thread. %s", e));
} catch (ExecutionException e) {
System.out.println(String.format("handle exception in child thread. %s", e));
} finally {
if (executorService != null) {
executorService.shutdown();
}
}
}
}
命令行輸出:do something 1
handle exception in child thread. java.util.concurrent.ExecutionException: java.lang.RuntimeException: ChildThread1 exception
總結(jié)
上面就是3種常用的Java子線程異常處理方法。其實(shí)樓主還想到了另外幾個(gè)特定場(chǎng)景下的解決辦法,改天再分析,謝謝大家支持~
這篇基于Java子線程中的異常處理方法(通用)就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
不知道面試會(huì)不會(huì)問(wèn)Lambda怎么用(推薦)
這篇文章主要介紹了Lambda表達(dá)式用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
SpringBoot注解篇之@Resource與@Autowired的使用區(qū)別
@Resource 注解和 @Autowired 注解都是在 Spring Framework 中進(jìn)行依賴注入的注解,那么你知道他們有什么區(qū)別嗎,本文就來(lái)介紹一下2023-12-12
Spring@Autowired與@Resource的區(qū)別有哪些
這篇文章主要為大家詳細(xì)介紹了@Autowired與@Resource的區(qū)別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-02-02
關(guān)于java.util.Random的實(shí)現(xiàn)原理詳解
Java實(shí)用工具類庫(kù)中的類java.util.Random提供了產(chǎn)生各種類型隨機(jī)數(shù)的方法,下面這篇文章主要給大家介紹了關(guān)于java.util.Random實(shí)現(xiàn)原理的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。2017-08-08
完整java開發(fā)中JDBC連接數(shù)據(jù)庫(kù)代碼和步驟
這篇文章主要介紹了完整java開發(fā)中JDBC連接數(shù)據(jù)庫(kù)代碼和步驟,需要的朋友可以參考下2015-09-09
JAVA實(shí)現(xiàn)利用第三方平臺(tái)發(fā)送短信驗(yàn)證碼
本文以注冊(cè)為例,在SpringMVC+Spring+Mybatis框架的基礎(chǔ)上完成該短信驗(yàn)證碼功能。需要的朋友一起來(lái)看下吧2016-12-12
初步認(rèn)識(shí)JVM的體系結(jié)構(gòu)
大家都知道,Java中JVM的重要性,學(xué)習(xí)了JVM你對(duì)Java的運(yùn)行機(jī)制、編譯過(guò)程和如何對(duì)Java程序進(jìn)行調(diào)優(yōu)相信都會(huì)有一個(gè)很好的認(rèn)知.在面試中JVM也是非常重要的一部分,比如JVM調(diào)優(yōu),JVM對(duì)象分配規(guī)則,內(nèi)存模型、方法區(qū),還有種要GC等,需要的朋友可以參考下2021-06-06

