UncaughtExceptionHandler 获取线程运行时异常

我们知道线程执行体的方法接口定义为:public void run(),因此线程在执行单元中是不允许抛出checked异常的,且线程之间是相对独立的,他们运行在自己的上下文当中,派生它的线程无法直接感知到它在运行时出现的异常信息。为了解决这个问题,java提供了UncaughtExceptionHandler接口,当线程在运行时发生异常时,会回调这个接口,从而得知哪个线程在运行时出错。

UncaughtExceptionHandler 接口定义如下:

使用@FunctionalInterface表示只有一个抽象方法的接口。

在线程发生异常时,jvm会调用Thread类中的dispatchUncaughtException方法

该方法调用了getUncaughtExceptionHandler()获取UncaughtExceptionHandler异常处理器的实例对象

通过源码我们可以看出,当线程发生异常时,jvm会调用Thread类中的dispatchUncaughtException方法,会判断当前线程是否设置了uncaughtExceptionHandler,如果设置了就执行自己的uncaughtException方法,否则就使用所在ThreadGroup的uncaughtException方法。ThreadGroup 异常处理源码如下:

    
public void uncaughtException(Thread t, Throwable e) {
        //t 发生异常的线程  e 异常信息
        //判断parent(父级ThreadGroup)是否为null
        if (parent != null) {
            //调用父级ThreadGroup的uncaughtException方法
            parent.uncaughtException(t, e);
        } else {
            //如果没有父级,则尝试获取全局的UncaughtExceptionHandler
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                //如果设置了全局的异常处理器,则调用全局的异常处理器的uncaughtException
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                //否则控制台输出错误信息
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
}

由ThreadGroup源码可以看出,这个方法其实啥也没做,不断得去找上级,没有上级就调用全局异常处理器,再没有就直接控制台打印了。因此,如果真要用ThreadGroup来获取异常,我们就需要继承ThreadGroup来重写这个方法了。

在这里,我们提到了全局异常处理器,全局异常处理器是在Thread类中定义的

现在我们编写代码来验证是否就是这样一个流程:

1、设置全局异常处理器

    public static void main(String[] args) {
        //设置接口回调
        //设置全局的异常处理器
        Thread.setDefaultUncaughtExceptionHandler((t,e)->{
            System.out.println(t.getName()+" occur exception");
            e.printStackTrace();
        });
        final Thread thread = new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            //unchecked异常
            System.out.println(1/0);
        },"Test-Thread");
        thread.start();
    }

运行结果:

2、为线程添加自定义异常处理器:

        //设置接口回调
        //设置全局的异常处理器
        Thread.setDefaultUncaughtExceptionHandler((t,e)->{
            System.out.println(t.getName()+" occur exception");
            e.printStackTrace();
        });
        final Thread thread = new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            //unchecked异常
            System.out.println(1/0);
        },"Test-Thread");
        //为某个线程设计异常处理器
        thread.setUncaughtExceptionHandler((t,e)-> {
            System.out.println(t.getName()+"发生异常");
            e.printStackTrace();
        });
        thread.start();
    }

运行结果:

自定义异常处理器中获取异常信息,而不是在全局异常处理器中。

3、将线程加入到group,在ThreadGroup中处理异常信息:

//继承ThreadGroup,重写uncaughtException处理异常信息
public class MyThreadGroup extends ThreadGroup {
    public MyThreadGroup(String name) {
        super(name);
    }

    public MyThreadGroup(ThreadGroup parent, String name) {
        super(parent, name);
    }
    public void uncaughtException(Thread thread, Throwable exception){
        System.out.println("ThreadGroup 捕获异常信息");
        exception.printStackTrace();
    }
}
    
    public static void main(String[] args) {
        ThreadGroup group1 = new MyThreadGroup("T1");
        //设置全局的异常处理器
        Thread.setDefaultUncaughtExceptionHandler((t,e)->{
            System.out.println(t.getName()+" occur exception");
            e.printStackTrace();
        });
        //创建线程并指定其ThreadGroup为group1
        final Thread thread = new Thread(group1,()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(1/0);
        },"Test-Thread");
        //设置线程自定义的异常处理器
        //thread.setUncaughtExceptionHandler((t,e)-> {
        //    System.out.println(t.getName()+"发生异常");
        //    e.printStackTrace();
        //});
        thread.start();
    }

注意,上面的代码我们注释掉了线程设置自定义处理器的部分,运行结果如下:

在ThreaGroup中捕获了异常信息,而不是使用全局的

解开注释,添加线程设置自定义处理器的部分,结果如下:

在自定义的异常处理器中捕获异常,而不是在ThraedGroup和全局异常处理器当中。

结论:

获取线程未捕获的异常有三种方式:

1、线程设置自定义的UncaughtExceptionHandler

2、设置全局的UncaughtExceptionHandler

3、在线程所属的ThreadGroup中捕获异常

通过上面的方式,线程发生异常时,派生它的线程就能获取到它的异常信息,知道是哪个子线程发生了错误。并且这三种方式有不同的优先级:自定义>ThreadGroup>全局

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值