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

常见错误

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

原因分析

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

  1. public abstract void run()
  1.  

解决办法

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

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

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

方法二:为线程设置“未捕获异常处理器”UncaughtExceptionHandler

  1. 为线程设置异常处理器。具体做法可以是以下几种:
  1. 1Thread.setUncaughtExceptionHandler设置当前线程的异常处理器;
  1. 2Thread.setDefaultUncaughtExceptionHandler为整个程序设置默认的异常处理器;
  1. 如果当前线程有异常处理器(默认没有),则优先使用该UncaughtExceptionHandler类;否则,如果当前线程所属的线程组有异常处理器,则使用线程组的
  1. UncaughtExceptionHandler;否则,使用全局默认的DefaultUncaughtExceptionHandler;如果都没有的话,子线程就会退出。
  1. 注意:子线程中发生了异常,如果没有任何类来接手处理的话,是会直接退出的,而不会记录任何日志。
  1. 所以,如果什么都不做的话,是会出现子线程任务既没执行成功,也没有任何日志提示的“诡异”现象的。
  1. 设置当前线程的异常处理器:
  1. 或者,设置所有线程的默认异常处理器
  1. public class ChildThread implements Runnable {
  2. private static ChildThreadExceptionHandler exceptionHandler;
  3.  
  4. static {
  5. exceptionHandler = new ChildThreadExceptionHandler();
  6. Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
  7. }
  8.  
  9. 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. }
  1.  
  1. 命令行输出:
  1. do something 1
  2. handle exception in child thread. java.lang.RuntimeException: ChildThread exception
  1.  

方法三:通过Future的get方法捕获异常(推荐)

  1. 使用线程池提交一个能获取到返回信息的方法,也就是ExecutorService.submit(Callable)
  1.  
  1. submit之后可以获得一个线程执行结果的Future对象,而如果子线程中发生了异常,通过future.get()获取返回值时,可以捕获到
  1. ExecutionException异常,从而知道子线程中发生了异常。
  1.  
  1. 子线程代码:
  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. }
  8.  
  9. private void exceptionMethod() {
  10. throw new RuntimeException("ChildThread1 exception");
  11. }
  12. }
  1.  
  1. 父线程代码:
  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. }
  1. 命令行输出:
  1. do something 1
  2. handle exception in child thread. java.util.concurrent.ExecutionException: java.lang.RuntimeException: ChildThread1 exception

总结

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

Spring Boot应用中的异常处理的更多相关文章

  1. 转:Spring Boot应用中的异常处理

    引自:https://www.cnblogs.com/yangfanexp/p/7616570.html 楼主前几天写了一篇“Java子线程中的异常处理(通用)”文章,介绍了在多线程环境下3种通用的异 ...

  2. 在spring boot环境中使用fastjson + redis的高速缓存技术

    因为项目需求,需要在spring boot环境中使用redis作数据缓存.之前的解决方案是参考的http://wiselyman.iteye.com/blog/2184884,具体使用的是Jackso ...

  3. 你真的理解 Spring Boot 项目中的 parent 吗?

    前面和大伙聊了 Spring Boot 项目的三种创建方式,这三种创建方式,无论是哪一种,创建成功后,pom.xml 坐标文件中都有如下一段引用: <parent> <groupId ...

  4. Spring Boot项目中使用Swagger2

    Swagger2是一款restful接口文档在线生成和在线接口调试工具,Swagger2在Swagger1.x版本的基础上做了些改进,下面是在一个Spring Boot项目中引入Swagger2的简要 ...

  5. spring boot JPA中实体类常用注解

    spring boot jpa中的注解很多,参数也比较多.没必要全部记住,但是经常查看官方文档也比较麻烦,记录一下一些常用的注解.通过一些具体的例子来帮助记忆. @Entity @Table(name ...

  6. Spring Boot 2中对于CORS跨域访问的快速支持

    原文:https://www.jianshu.com/p/840b4f83c3b5 目前的程序开发,大部分都采用前后台分离.这样一来,就都会碰到跨域资源共享CORS的问题.Spring Boot 2 ...

  7. spring boot配置文件中 spring.mvc.static-path-pattern 配置项

    spring boot项目中的静态资源文件存放在static文件下面,当通过浏览器访问这些静态文件时,发现必须要添加static作为前缀才能访问,折腾了一番后发现,这个前缀跟 spring.mvc.s ...

  8. RabbitMQ入门:在Spring Boot 应用中整合RabbitMQ

    在上一篇随笔中我们认识并安装了RabbitMQ,接下来我们来看下怎么在Spring Boot 应用中整合RabbitMQ. 先给出最终目录结构: 搭建步骤如下: 新建maven工程amqp 修改pom ...

  9. spring boot test中mockito的运用

    mock的意义 在微服务盛行的当下,开发过程中往往出现A应用中某功能的实现需要调用B应用的接口,无论使用RPC还是restful都需要B应用提供接口的实现整个开发工作才能继续进行.从而导致A应用的开发 ...

随机推荐

  1. nodejs笔记--mysql篇(四)

    测试连接 var mysql = require('mysql'); //调用MySQL模块 //创建一个connection var connection = mysql.createConnect ...

  2. Windows Phone编程回顾

    前言 已有一年多没有碰WP相关的开发了. 近期经常看博客园的文章, 发现开发WP应用的同学很多, 其中博问频道关于"WPF", "C#", "WP8& ...

  3. PHP 将一个字符串部分字符用$re替代隐藏

    <?php/** * 将一个字符串部分字符用$re替代隐藏 * @param string $string 待处理的字符串 * @param int $start 规定在字符串的何处开始, * ...

  4. TensorFlow源码框架 杂记

    一.为什么我们需要使用线程池技术(ThreadPool) 线程:采用“即时创建,即时销毁”策略,即接受请求后,创建一个新的线程,执行任务,完毕后,线程退出: 线程池:应用软件启动后,立即创建一定数量的 ...

  5. 基于TensorFlow解决手写数字识别的Softmax方法、多层卷积网络方法和前馈神经网络方法

    一.基于TensorFlow的softmax回归模型解决手写字母识别问题 详细步骤如下: 1.加载MNIST数据: input_data.read_data_sets('MNIST_data',one ...

  6. JSON解析与序列化

    JSON之所以流行,拥有与JavaScript类似的语法并不是全部原因.更重要的一个原因是,可以把JSON数据结构解析为有用的 JavaScript对象.与XML数据结构要解析成DOM文档而且从中提取 ...

  7. DataSet和List 泛型之间互相转换 (转载)

    //DataSet与泛型集合间的互相转换 //利用反射机制将DataTable的字段与自定义类型的公开属性互相赋值. //注意:从DataSet到IList<T>的转换,自定义类型的公开属 ...

  8. CCleaner专业版注册码

    下载软件安装之后: 1.断网 2.用户名:任意,注册码:C2YW-XZT7-A4SE-UD89-YZPC

  9. array_intersect_assoc 与array_intersect区别

    1.array_intersect_assoc — 带索引检查计算数组的交集 说明 array array_intersect_assoc ( array $array1 , array $array ...

  10. MySQL优化之profile

    分析SQL执行带来的开销是优化SQL的重要手段.在MySQL数据库中,可以通过配置profiling参数来启用SQL剖析.该参数可以在全局和session级别来设置.对于全局级别则作用于整个MySQL ...