Spring Boot应用中的异常处理
在普通的单线程程序中,捕获异常只需要通过try ... catch ... finally ...代码块就可以了。那么,在并发情况下,比如在父线程中启动了子线程,如何正确捕获子线程中的异常,从而进行相应的处理呢?
常见错误
也许有人会觉得,很简单嘛,直接在父线程启动子线程的地方try ... catch一把就可以了,其实这是不对的。
原因分析
让我们回忆一下Runnable接口的run方法的完整签名,因为没有标识throws语句,所以方法是不会抛出checked异常的。至于RuntimeException这样的unchecked异常,由于新线程由JVM进行调度执行,如果发生了异常,也不会通知到父线程。
public abstract void run()
解决办法
那么,如何正确处理子线程中的异常呢?楼主想到了3种常用方法,分享给大家。
前2种方法都是在子线程中处理,第3种方法是在父线程中处理。
具体用哪一种方法,取决于这个异常是否适合在子线程中处理。例如有些异常更适合由调用方(父线程)处理,那么此时就应当用第3种方法。
方法一:子线程中try... catch...
最简单有效的办法,就是在子线程的执行方法中,把可能发生异常的地方,用try ... catch ... 语句包起来。
子线程代码:
方法二:为线程设置“未捕获异常处理器”UncaughtExceptionHandler
为线程设置异常处理器。具体做法可以是以下几种:
(1)Thread.setUncaughtExceptionHandler设置当前线程的异常处理器;
(2)Thread.setDefaultUncaughtExceptionHandler为整个程序设置默认的异常处理器;
如果当前线程有异常处理器(默认没有),则优先使用该UncaughtExceptionHandler类;否则,如果当前线程所属的线程组有异常处理器,则使用线程组的
UncaughtExceptionHandler;否则,使用全局默认的DefaultUncaughtExceptionHandler;如果都没有的话,子线程就会退出。
注意:子线程中发生了异常,如果没有任何类来接手处理的话,是会直接退出的,而不会记录任何日志。
所以,如果什么都不做的话,是会出现子线程任务既没执行成功,也没有任何日志提示的“诡异”现象的。
设置当前线程的异常处理器:
或者,设置所有线程的默认异常处理器
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
方法三:通过Future的get方法捕获异常(推荐)
使用线程池提交一个能获取到返回信息的方法,也就是ExecutorService.submit(Callable)
在submit之后可以获得一个线程执行结果的Future对象,而如果子线程中发生了异常,通过future.get()获取返回值时,可以捕获到
ExecutionException异常,从而知道子线程中发生了异常。
子线程代码:
public class ChildThread implements Callable<String> {
public String call() throws Exception {
System.out.println("do something 1");
exceptionMethod();
System.out.println("do something 2");
return "test result";
} 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
总结
以上就是3种通用的Java子线程异常处理方法。
具体使用哪种,取决于异常是否适合在子线程中处理,楼主更推荐第3种方式,可以方便地在父线程中根据子线程抛出的异常做适当的处理(自己处理,或者忽略,或者继续向上层调用方抛异常)。
其实楼主还想到了另外几个特定场景下的解决办法,改天再分析,谢谢大家支持~
Spring Boot应用中的异常处理的更多相关文章
- 转:Spring Boot应用中的异常处理
引自:https://www.cnblogs.com/yangfanexp/p/7616570.html 楼主前几天写了一篇“Java子线程中的异常处理(通用)”文章,介绍了在多线程环境下3种通用的异 ...
- 在spring boot环境中使用fastjson + redis的高速缓存技术
因为项目需求,需要在spring boot环境中使用redis作数据缓存.之前的解决方案是参考的http://wiselyman.iteye.com/blog/2184884,具体使用的是Jackso ...
- 你真的理解 Spring Boot 项目中的 parent 吗?
前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...
- Spring Boot项目中使用Swagger2
Swagger2是一款restful接口文档在线生成和在线接口调试工具,Swagger2在Swagger1.x版本的基础上做了些改进,下面是在一个Spring Boot项目中引入Swagger2的简要 ...
- spring boot JPA中实体类常用注解
spring boot jpa中的注解很多,参数也比较多.没必要全部记住,但是经常查看官方文档也比较麻烦,记录一下一些常用的注解.通过一些具体的例子来帮助记忆. @Entity @Table(name ...
- Spring Boot 2中对于CORS跨域访问的快速支持
原文:https://www.jianshu.com/p/840b4f83c3b5 目前的程序开发,大部分都采用前后台分离.这样一来,就都会碰到跨域资源共享CORS的问题.Spring Boot 2 ...
- spring boot配置文件中 spring.mvc.static-path-pattern 配置项
spring boot项目中的静态资源文件存放在static文件下面,当通过浏览器访问这些静态文件时,发现必须要添加static作为前缀才能访问,折腾了一番后发现,这个前缀跟 spring.mvc.s ...
- RabbitMQ入门:在Spring Boot 应用中整合RabbitMQ
在上一篇随笔中我们认识并安装了RabbitMQ,接下来我们来看下怎么在Spring Boot 应用中整合RabbitMQ. 先给出最终目录结构: 搭建步骤如下: 新建maven工程amqp 修改pom ...
- spring boot test中mockito的运用
mock的意义 在微服务盛行的当下,开发过程中往往出现A应用中某功能的实现需要调用B应用的接口,无论使用RPC还是restful都需要B应用提供接口的实现整个开发工作才能继续进行.从而导致A应用的开发 ...
随机推荐
- 为什么请求时,需要使用URLEncode做encode转码操作(转)
什么要对url进行encode 发现现在几乎所有的网站都对url中的汉字和特殊的字符,进行了urlencode操作,也就是: http://hi.baidu.com/%BE%B2%D0%C4%C0%C ...
- Thunder团队第五周 - Scrum会议3
Scrum会议3 小组名称:Thunder 项目名称:i阅app Scrum Master:王航 工作照片: 苗威同学在拍照,所以不在照片内. 参会成员: 王航(Master):http://www. ...
- Java学习个人备忘录之构造函数&this
构造函数 概念:构建创造对象时调用的函数. 作用:可以给对象进行初始化,创建对象都必须要通过构造函数初始化. 一个类中如果没有定义过构造函数,那么该类中会有一个默认的空参数构造函数.如果在类中定义了指 ...
- lol人物模型提取(三)
提取出来的lol人物模型能让你知道一些有趣的信息,比如说给英雄量个身高啥的. 经测量,佐伊的身高应大于1m60,比想象中的着实高不少啊. 然后还应该把这个模型镜像对称一下,在3dsmax里 ...
- Linux服务器记录并查询历史操作记录
Linux服务器在使用过程中,经常会有除自己之外的其他人员使用.并不是每个人都对Linux服务器特别熟悉,难免会有一些操作导致服务器报错. 因此,监控Linux服务器的操作并记录下来,是非常有必要的! ...
- 每天网络半小时(MAC数据包在哪里合并的)
ip_deliver_local函数中函数中完成合并 听过netfilter框架中也会 因为net_filter框架需要感知到第四层的信息,但是单个数据包是无法感知到这些信息的,所以需要在netfil ...
- 苹果IOS、安卓推送功能开发
IOS推送开发:以下是基于开源javapns推送开发1.DerInputStream.getLength(): lengthTag=111, too big.先排除是否由于打包时证书 .p12 文件被 ...
- Spring事务管理Transaction
Spring提供了许多内置事务管理器实现: DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器, ...
- zk分布锁的java实现
只做记录,直接上代码 父类: package com.ylcloud.common.lock; import com.alibaba.fastjson.JSON; import org.I0Itec. ...
- 【Python】从简单案列中揭示常用内置函数以及数据类型
前面提到了BIF(内置函数)这个概念,什么是内置函数,就是python已经定义好的函数,不需要人为再自己定义,直接拿来就可以用的函数,那么都有哪些BIF呢? 可以在交互式界面(IDLE)输入这段代码, ...