当一个线程在执行过程中抛出了异常,并且没有进行try..catch,那么这个线程就会终止运行 。在Thread类中,提供了两个可以设置线程未捕获异常的全局处理器,我们可以在处理器里做一些工作,例如将异常信息发送到远程服务器 。虽然这可以捕获到线程中的异常,但是并不能阻止线程停止运行 。因此该在线程run方法里try..catch的,还是要好好的进行try..catch 。
从Thread类源代码中可以看到这2个变量:
【Java线程未捕获异常处理 UncaughtExceptionHandler】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接口是怎么声明的:
@FunctionalInterfacepublic 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 beforeruning捕获异常,Thread-0,exception
可以看出,虽然两个异常处理器都有设置,并且defaultUncaughtExceptionHandler是最后设置的,不过起效的是uncaughtExceptionHandler 。
可以将thread.setUncaughtExceptionHandler(...);注释掉:输出:
run beforeruningDefault捕获异常,Thread-0,exception
注释后 , defaultUncaughtExceptionHandler起效了,证明了uncaughtExceptionHandler优先级高于defaultUncaughtExceptionHandler 。
推荐阅读
- 死磕面试系列,Java到底是值传递还是引用传递?
- 【Java复健指南09】项目练习全解--房屋出租系统
- Java集合精选常见面试题
- Java单例模式,看这一篇就够了
- 微信支付v3接口的 官方 Java SDK
- 华为p50pro有什么颜色〖科技未来〗-华为p50pro配色介绍
- Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化
- 一 Pthread 并发编程——深入剖析线程基本元素和状态
- 夯实Java基础,一篇文章全解析线程问题
- 四 Java多线程-ThreadPool线程池-2