问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

Java子线程中的异常处理(通用)

发布网友 发布时间:2024-10-01 10:44

我来回答

1个回答

热心网友 时间:2024-12-13 12:36

在普通的单线程程序中,捕获异常只需要通过try ... catch ... finally ...代码块就可以了。那么,在并发情况下,比如在父线程中启动了子线程,如何正确捕获子线程中的异常,从而进行相应的处理呢?

常见错误

也许有人会觉得,很简单嘛,直接在父线程启动子线程的地方try ... catch一把就可以了,其实这是不对的。

原因分析

让我们回忆一下Runnable接口的run方法的完整签名,因为没有标识throws语句,所以方法是不会抛出checked异常的。至于RuntimeException这样的unchecked异常,由于新线程由JVM进行调度执行,如果发生了异常,也不会通知到父线程。

public abstract void run()解决办法

那么,如何正确处理子线程中的异常呢?楼主想到了3种常用方法,分享给大家。 前2种方法都是在子线程中处理,第3种方法是在父线程中处理。 具体用哪一种方法,取决于这个异常是否适合在子线程中处理。例如有些异常更适合由调用方(父线程)处理,那么此时就应当用第3种方法。

方法一:子线程中try... catch...

最简单有效的办法,就是在子线程的执行方法中,把可能发生异常的地方,用try ... catch ... 语句包起来。

子线程代码:

1 public class ChildThread implements Runnable { 2 public void run() { 3 doSomething1(); 4 try { 5 // 可能发生异常的方法 6 exceptionMethod(); 7 } catch (Exception e) { 8 // 处理异常 9 System.out.println(String.format("handle exception in child thread. %s", e));10 }11 doSomething2();12 }13 }方法二:为线程设置“未捕获异常处理器”UncaughtExceptionHandler

为线程设置异常处理器。具体做法可以是以下几种:

(1)Thread.setUncaughtExceptionHandler设置当前线程的异常处理器;

(2)Thread.setDefaultUncaughtExceptionHandler为整个程序设置默认的异常处理器;

如果当前线程有异常处理器(默认没有),则优先使用该UncaughtExceptionHandler类;否则,如果当前线程所属的线程组有异常处理器,则使用线程组的

UncaughtExceptionHandler;否则,使用全局默认的DefaultUncaughtExceptionHandler;如果都没有的话,子线程就会退出。

注意:子线程中发生了异常,如果没有任何类来接手处理的话,是会直接退出的,而不会记录任何日志。

所以,如果什么都不做的话,是会出现子线程任务既没执行成功,也没有任何日志提示的“诡异”现象的。

设置当前线程的异常处理器:

1 public class ChildThread implements Runnable { 2 private static ChildThreadExceptionHandler exceptionHandler; 34 static { 5 exceptionHandler = new ChildThreadExceptionHandler(); 6 } 78 public void run() { 9 Thread.currentThread().setUncaughtExceptionHandler(exceptionHandler);10 System.out.println("do something 1");11 exceptionMethod();12 System.out.println("do something 2");13 }14 15 public static class ChildThreadExceptionHandler implements Thread.UncaughtExceptionHandler {16 public void uncaughtException(Thread t, Throwable e) {17 System.out.println(String.format("handle exception in child thread. %s", e));18 }19 }20 }

或者,设置所有线程的默认异常处理器

1 public class ChildThread implements Runnable { 2 private static ChildThreadExceptionHandler exceptionHandler; 34 static { 5 exceptionHandler = new ChildThreadExceptionHandler(); 6 Thread.setDefaultUncaughtExceptionHandler(exceptionHandler); 7 } 89 public void run() {10 System.out.println("do something 1");11 exceptionMethod();12 System.out.println("do something 2");13 }14 15 private void exceptionMethod() {16 throw new RuntimeException("ChildThread exception");17 }18 19 public static class ChildThreadExceptionHandler implements Thread.UncaughtExceptionHandler {20 public void uncaughtException(Thread t, Throwable e) {21 System.out.println(String.format("handle exception in child thread. %s", e));22 }23 }24 }

命令行输出:

do something 1handle exception in child thread. java.lang.RuntimeException: ChildThread exception方法三:通过Future的get方法捕获异常(推荐)

使用线程池提交一个能获取到返回信息的方法,也就是ExecutorService.submit(Callable) 在submit之后可以获得一个线程执行结果的Future对象,而如果子线程中发生了异常,通过future.get()获取返回值时,可以捕获到ExecutionException异常,从而知道子线程中发生了异常。

子线程代码:

1 public class ChildThread implements Callable<String> { 2 public String call() throws Exception { 3 System.out.println("do something 1"); 4 exceptionMethod(); 5 System.out.println("do something 2"); 6 return "test result"; 7 } 89 private void exceptionMethod() {10 throw new RuntimeException("ChildThread1 exception");11 }12 }

父线程代码:

1 public class Main { 2 public static void main(String[] args) { 3 ExecutorService executorService = Executors.newFixedThreadPool(8); 4 Future future = executorService.submit(new ChildThread()); 5 try { 6 future.get(); 7 } catch (InterruptedException e) { 8 System.out.println(String.format("handle exception in child thread. %s", e)); 9 } catch (ExecutionException e) {10 System.out.println(String.format("handle exception in child thread. %s", e));11 } finally {12 if (executorService != null) {13 executorService.shutdown();14 }15 }16 }17 }

命令行输出:

do something 1handle exception in child thread. java.util.concurrent.ExecutionException: java.lang.RuntimeException: ChildThread1 exception总结

以上就是3种通用的Java子线程异常处理方法。 具体使用哪种,取决于异常是否适合在子线程中处理,楼主更推荐第3种方式,可以方便地在父线程中根据子线程抛出的异常做适当的处理(自己处理,或者忽略,或者继续向上层调用方抛异常)。 其实楼主还想到了另外几个特定场景下的解决办法,改天再分析,谢谢大家支持。

原文:https://juejin.cn/post/7102705144323637284
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
手机壁纸花卉红色手机壁纸花卉 辽宁智能五芯防水连接器 军工产品浅谈——航空插头 xp电脑系统修复XP系统怎么恢复出厂设置 ...表编辑器后打不开任何文件,无需重装系统,电脑盲都能用 粼基本信息 粼五笔怎样打? 【微科普】想变成光?先搞清楚光是什么吧! 手机被拉黑后打电话是什么提示? 物流管理专业对数学的要求高么 爷青回和爷青结是什么意思_爷青回和爷青结的区别 科沃斯cr333风机电机是几伏电压 军绿色外套、风衣、裤子怎么搭配 我的光大信用卡还款日每月15日。我是按短信提醒最低还款还。为什么还有... 请问防护员证有什么用 铁路客规有规定客运员防护捡道渣(垃圾)吗? 李彦宏:百度"去KPI化":引入OKR考核模式,要治大公司病_百度知... 中国茶道的历史渊源:讲述茶道故事 中国茶道道家“天人和一”的哲学思想 乐视2手机老是死机,突然聊着天就卡了,然后黑屏重启,刚买的第8天 喝多纯牛奶会长胖吗 长沙出火车站核酸检测为什么收不到检测结果? 去长沙是做了核酸过去还是到了地方做 ...打开wifi?为什么我打开需要密码?路由器要怎么设置为无线安全呢?_百 ... 种植草莓季节到了,军哥教你如何移栽草莓,这些栽植要点需要掌握 滴滴出行如何打往返车呢? 有没有飞儿乐队“fir”的消息?最新专辑,好像只有《月牙湾》最棒。为 ... 军犬什么意思 男人对外面的女人动了真心后,会有哪些表现? ...的女儿喜欢花猫,然后那只黑猫死了,然后我就醒了,请问这预示着什么... iphone xs max序列号怎么看是不是二手货翻新呢?怎么看激活日期呢?跪求... 【合集】抗日电视大全,【免费高清 免费看mv大片的软件合集202? 如何去除鱼池青苔 vivoy27为什么不显示通话记录 有谁清楚联通签到送流量,怎么领 2022年妇女节是国家法定节假日吗 2022年妇女节是不是国家法定节假日 妇女节是法定节假日吗 三八是不是法定节假日 樱菲兰护肤品怎么样 请问这是啥虫子啊,从榻榻米的藤垫子下面爬出来,然后爬到墙上 张玲玲蟑螂美文摘抄 ...知道这是什么虫子,怎么消灭?墙上,泡沫垫子边下面,有很多。怕光。会... 车里面有蟑螂怎么办? ...和开关,求高手解答.还有额外...最好有截图谢谢 ...Edit Pro V2.1改变歌曲的调?求高手解答,最好附图!根据答案质量给予加 ... ...几何体未通过一致性检查,新手入门求高手解答,谢谢。 ...么~求高手,最好能是专门高中的女生来解答下~谢谢~越详细越好_百度知 ... 苹果8拍照抖动怎么解决 {请输入您的邮储个人网上银行注册证件号码或登录昵称。}这个地方应该填... 请输入您的邮储个人网上银行注册证件号码或登录昵称是填写什么呢?我填...