• Java线程未捕获异常处理 UncaughtExceptionHandler


    当一个线程在执行过程中抛出了异常,并且没有进行try..catch,那么这个线程就会终止运行。
    在Thread类中,提供了两个可以设置线程未捕获异常的全局处理器,我们可以在处理器里做一些工作,例如将异常信息发送到远程服务器。
    虽然这可以捕获到线程中的异常,但是并不能阻止线程停止运行。因此该在线程run方法里try..catch的,还是要好好的进行try..catch。

    从Thread类源代码中可以看到这2个变量:

    private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
    
    private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
    

    需要注意到区别,defaultUncaughtExceptionHandler是静态的,我们可以调用此方法设置所有线程对象的异常处理器,而uncaughtExceptionHandler则是针对单个线程对象的异常处理器。

    uncaughtExceptionHandler优先级高于defaultUncaughtExceptionHandler。

    Thread类提供了这2个变量的setter/getter:

    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkPermission(
                new RuntimePermission("setDefaultUncaughtExceptionHandler")
              );
        }
         defaultUncaughtExceptionHandler = eh;
     }
    
        
    public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
        return defaultUncaughtExceptionHandler;
    }
    
    
    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }
    
    public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
        checkAccess();
        uncaughtExceptionHandler = eh;
    }
    

    可以看到,getUncaughtExceptionHandler()中进行了判断,当uncaughtExceptionHandler为null时返回group。

    我们来看下UncaughtExceptionHandler接口是怎么声明的:

    @FunctionalInterface
    public interface UncaughtExceptionHandler {
        void uncaughtException(Thread t, Throwable e);
    }
    

    我们只需要实现UncaughtExceptionHandler接口,重写uncaughtException方法即可进行异常处理。

    那么JVM是怎么检测到线程发生异常,并将异常分发到处理器的呢?
    对于这块代码,JDK源码中看不到是如何处理的,可能需要翻阅hotspot源码,不过Thread类中提供了一个dispatchUncaughtException方法,将异常回调到了uncaughtExceptionHandler中去处理。

    private void dispatchUncaughtException(Throwable e) {
        getUncaughtExceptionHandler().uncaughtException(this, e);
    }
    

    很明显,dispatchUncaughtException应该就是提供给hotspot进行JNI回调的。
    而对于defaultUncaughtExceptionHandler的调用,猜测应该是在hotspot中直接完成了。

    接下来我们用示例来演示一下异常处理器的效果。

    示例:

    Thread thread = new Thread(() -> {
        System.out.println("run before");
    
        System.out.println("runing");
        if(1 == 1) {
            throw new IllegalStateException("exception");
        }
    
        System.out.println("run after");
    });
    thread.setUncaughtExceptionHandler((t, e) -> System.out.println("捕获异常," + t.getName() + "," + e.getMessage()));
    Thread.setDefaultUncaughtExceptionHandler((t, e) -> System.out.println("Default捕获异常," + t.getName() + "," + e.getMessage()));
    thread.start();
    

    输出:

    run before
    runing
    捕获异常,Thread-0,exception
    

    可以看出,虽然两个异常处理器都有设置,并且defaultUncaughtExceptionHandler是最后设置的,不过起效的是uncaughtExceptionHandler。

    可以将thread.setUncaughtExceptionHandler(...);注释掉:
    输出:

    run before
    runing
    Default捕获异常,Thread-0,exception
    

    注释后,defaultUncaughtExceptionHandler起效了,证明了uncaughtExceptionHandler优先级高于defaultUncaughtExceptionHandler。

  • 相关阅读:
    鸿蒙 gnss 发起定位流程
    cglib FastClass机制
    LeetCode算法日记:1224. 最大相等频率
    《The Swift Programming Language》之语言参考:类型
    半路入行网络安全,怎么学才不会走弯路
    将文件名称中空格以左的部分全部删除重命名
    Istio(九):istio安全之授权
    卷积神经网络(CNN)实现服装图像分类
    vue手动拖入和导入excel模版
    ICC2: ICG clone与ICG merge
  • 原文地址:https://www.cnblogs.com/ladderx/p/16857738.html