• Thread线程异常的处理方式


    前言

            我们都知道当程序运行出错时 , 会出现异常 , 并有异常信息输出  , 且如果不对异常进行捕获处理的话 , 是会影响到代码的执行的 . 

            但是如果在线程中出现异常, 则不然, 线程中出现的异常是没办法进行上抛捕获的 , 一般大家遇到这种情况都是在线程内进行异常捕获处理 , 但如果我现在需要将异常抛出,  告知主进程呢 ?

    异常种类

    首先我们需要知道 , 异常分为两类 ,分别是编译时异常和运行时异常 ,

    1.编译时异常

            定义方法时,必须声明所有可能抛出的异常。在调用该方法时,必须手动捕获它的checked Exception,即受检异常。如IO异常,SQL异常等。因此:编译时实际可以理解为需要被手动捕获的异常,实际就是编译器会主动检查的异常,如B方法中主动抛出了异常,若方法A中调用B方法,也需要手动捕获该异常,当没有手动捕获异常时,编译器会报错

    2.运行时异常

            运行时异常(RuntimeException),在定义方法时是不需要声明的,如空指针异常。在调用该方法时也不需要手动捕获该异常,也就是说,运行时异常也可以理解为未捕获的异常,运行过程中如果发生了此类异常,会被直接抛给jvm,由jvm来处理该异常

    线程异常分析

            线程设计的理念是自己线程内部的异常由自己线程内部来处理,因此假设在main主线程中创建了一个子线程,如果子线程出现了异常,main主线程是无法感知和catch到的(即使子线程出现了异常,父线程不会有任何影响,依然会正常运行)

    请看以下示例代码 , 我们在main函数里面new 一个线程 , 然后手动制造一个运行时异常 

    1. public class Thread03 {
    2. public static void main(String[] args) {
    3. try{
    4. Thread thread = new Thread(() -> {
    5. int i = 10/0;
    6. });
    7. thread.start();
    8. }catch (Exception e){
    9. System.out.println("异常了......");
    10. }
    11. }
    12. }

    分母不能为0  , 运行后报错如下 , 因为异常无法抛出去,所以异常由JVM接管,我们手动无法捕获。

             我们通过查看Thread的源码 , 可以看到如下代码 , UncaughtExceptionHandler , 当线程由于未捕获的异常而突然终止时调用的处理程序的接口。

    1. public interface UncaughtExceptionHandler {
    2. /**
    3. * Method invoked when the given thread terminates due to the
    4. * given uncaught exception.
    5. * <p>Any exception thrown by this method will be ignored by the
    6. * Java Virtual Machine.
    7. * @param t the thread
    8. * @param e the exception
    9. */
    10. void uncaughtException(Thread t, Throwable e);
    11. }
    12. // 向处理程序调度未捕获的异常。此方法旨在仅由JVM调用。
    13. private void dispatchUncaughtException(Throwable e) {
    14. getUncaughtExceptionHandler().uncaughtException(this, e);
    15. }

            注意 ,在这里 , 线程内的一个异常处理顺序 , 当线程因未捕获异常即将终止时,Java虚拟机将使用getUncaughtExceptionHandler查询线程的未捕获异常处理程序,并调用处理程序的uncaughtException方法,将线程和异常作为参数传递。如果线程未显式设置其UncaughtExceptionHandler,则其ThreadGroup对象将充当其UncaughtExceptionHandler。如果ThreadGroup对象没有处理异常的特殊要求,它可以将调用转发给默认的未捕获异常处理程序。

            然后我们通过下图的位置查看ThreadGroup源码 , 可以发现ThreadGroup自身就是一个handler,查看ThreadGroup的源码就可以发现,ThreadGroup实现了Thread.UncaughtExceptionHandler接口,并实现了默认的处理方法。默认的未捕获异常处理器处理时,会调用 System.err 进行输出,也就是直接打印到控制台了。

             由此可知,最终JVM是调用未捕获的异常处理器的uncaughtException()方法来处理异常的,并且是直接打印到控制台。

     线程外捕获线程异常

            前面在说线程内异常处理方式时候 , 说到了线程内异常的处理方式是通过设置线程的异常回调接口UncaughtExceptionHandler实际处理该线程异常的仍然是该子线程,只是通过回调接口,把处理异常的方法从具体的可执行对象本身的run方法中抽离了出去,使用这种方法,可以监控哪个子线程发生了异常,因为每一个线程发生异常都会回调该接口)

    UncaughtExceptionHandler回调接口

            线程发生异常时 , JVM会首先调用Thread类的dispatchUncaughtException,该方法调用getUncaughtExceptionHandler().uncaughtException(this,e  ,    getUncaughtExceptionHandler()方法中的执行逻辑是 -> 若当前线程的uncaughtExceptionHandler不为空,则使用当前线程的uncaughtExceptionHandler,否则使用线程组的uncaughtExceptionHandler

            根据该回调接口可知 , 如果想要获得线程的运行时异常,就需要设置当前线程的uncaughtExceptionHandler实例,并实现该回调接口的uncaughtException(this,e)方法

    自定义UncaughtExceptionHandler捕获异常

    定义一个线程异常处理器

    1. public class MyThreadExceptionHandler implements Thread.UncaughtExceptionHandler {
    2. /**
    3. * Method invoked when the given thread terminates due to the
    4. * given uncaught exception.
    5. * <p>Any exception thrown by this method will be ignored by the
    6. * Java Virtual Machine.
    7. *
    8. * @param t the thread
    9. * @param e the exception
    10. */
    11. @Override
    12. public void uncaughtException(Thread t, Throwable e) {
    13. System.out.println("自定义异常处理逻辑......");
    14. }
    15. }

    set 自定义异常处理器

    1. public class Thread03 {
    2. public static void main(String[] args) {
    3. try{
    4. Thread thread = new Thread(() -> {
    5. int i = 10/0;
    6. });
    7. // set 自定义异常处理器
    8. thread.setUncaughtExceptionHandler(new MyThreadExceptionHandler());
    9. thread.start();
    10. }catch (Exception e){
    11. System.out.println("异常了......");
    12. }
    13. }
    14. }

            我们通过给Thread设置自定义的异常捕获处理器,覆盖jvm默认的异常处理器,就可以实现异常的自定义处理。

  • 相关阅读:
    MongoDB-快速上手MongoDB命令行的一些简单操作
    远程服务器上,再次配笔记1、2、11、12相同的深度学习和gcc环境
    剑指 Offer 50. 第一个只出现一次的字符
    「Java核心面试系列」Spring面试核心49问(附答案)
    盘点10个程序员可以接私活的平台和一些建议
    Java 网络编程
    绘制第一个三角形
    「Java开发指南」如何在Spring中使用JAX-WS注释器?
    校园邮箱-注册
    PINN期刊推荐总结
  • 原文地址:https://blog.csdn.net/qq_42543063/article/details/125594717