Thread线程异常的处理方式

前言

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

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

异常种类

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

1.编译时异常

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

2.运行时异常

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

线程异常分析

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

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

public class Thread03 {

    public static void main(String[] args) {
        try{
            Thread thread = new Thread(() -> {
                int i = 10/0;
            });
            thread.start();
        }catch (Exception e){
            System.out.println("异常了......");
        }

    }
}

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

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

public interface UncaughtExceptionHandler {
        /**
         * Method invoked when the given thread terminates due to the
         * given uncaught exception.
         * <p>Any exception thrown by this method will be ignored by the
         * Java Virtual Machine.
         * @param t the thread
         * @param e the exception
         */
        void uncaughtException(Thread t, Throwable e);
    }


    // 向处理程序调度未捕获的异常。此方法旨在仅由JVM调用。
    private void dispatchUncaughtException(Throwable e) {
        getUncaughtExceptionHandler().uncaughtException(this, e);
    }

        注意 ,在这里 , 线程内的一个异常处理顺序 , 当线程因未捕获异常即将终止时,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捕获异常

定义一个线程异常处理器

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

set 自定义异常处理器

public class Thread03 {

    public static void main(String[] args) {
        try{
            Thread thread = new Thread(() -> {
                int i = 10/0;
            });
            // set 自定义异常处理器 
            thread.setUncaughtExceptionHandler(new MyThreadExceptionHandler());
            thread.start();
        }catch (Exception e){
            System.out.println("异常了......");
        }

    }
}

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

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值