当一个线程在执行过程中抛出了异常,并且没有进行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。

Java线程未捕获异常处理 UncaughtExceptionHandler的更多相关文章

  1. Java & Android未捕获异常处理机制

    一.背景 无论是Java还是Android项目,往往都会用到多线程.不管是主线程还是子线程,在运行过程中,都有可能出现未捕获异常.未捕获异常中含有详细的异常信息堆栈,可以很方便的去帮助我们排查问题. ...

  2. Android 之 应用未捕获异常处理

    最近开发一款低功耗蓝牙通讯的 Android 应用,安装使用时多次出现“ 抱歉,xxx已停止 ”.现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出 ...

  3. 自己定义Application的未捕获异常处理

    近期由于工作原因.进行Android应用开发时发现应用在出现类似空指针等异常时,抛出未被捕获的异常.Android系统有默认的未捕获异常处理器,默认行为是结束对应的线程,但并不会直接退出程序,并且在应 ...

  4. Java线程中的异常处理

    对于对线程,当主线程中有子线程运行出现异常时,主线程是不能捕获到该异常的,子线程会直接退出,不会记录任何日志. 解决: 1.子线程中try catch. 2.设置线程的未捕获异常处理器,Uncaugh ...

  5. JAVA线程池ScheduledExecutorService周期性地执行任务 与单个Thread周期性执行任务的异常处理

    本文记录: 1,使用ScheduledExecutorService的 scheduleAtFixedRate 方法执行周期性任务的过程,讨论了在任务周期执行过程中出现了异常,会导致周期任务失败. 2 ...

  6. Java子线程中的异常处理(通用)

    在普通的单线程程序中,捕获异常只需要通过try ... catch ... finally ...代码块就可以了.那么,在并发情况下,比如在父线程中启动了子线程,如何正确捕获子线程中的异常,从而进行相 ...

  7. java线程异常处理方法

    工作中常发现有些程序发生异常但却没有错误日志,原因就是一些开发线程异常处理错误,导致程序报错但异常信息打印到堆栈上,不好在生产环境中定位问题. 在java多线程程序中,所有线程都不允许抛出未捕获的ch ...

  8. JAVA 线程中的异常捕获

    在java多线程程序中,所有线程都不允许抛出未捕获的checked exception(比如sleep时的InterruptedException),也就是说各个线程需要自己把自己的checked e ...

  9. 捕获Java线程池执行任务抛出的异常

    捕获Java线程池执行任务抛出的异常Java中线程执行的任务接口java.lang.Runnable 要求不抛出Checked异常, public interface Runnable { publi ...

随机推荐

  1. 汇编/C/C++/MFC/JAVA/C# 进阶群103197177

    欢迎广大喜欢编程朋友加入进来.如果是大神请分享你的经验,带领广大小伙伴一起打怪升级得经验:如果是编程新人,那么这里是你不二的选择,分享,奉献是我们追求的目标:我们之中大部分是有一年多工作经验的热血编程 ...

  2. session 总结

    session 总结(单节点场景) session 称作域对象,一般保存在当前服务器的内存中,如果有很多session也会部分不常用的session"钝化"到磁盘中,若磁盘中的se ...

  3. RabbitMQ之消息模式简单易懂,超详细分享~~~

    前言 上一篇对RabbitMQ的流程和相关的理论进行初步的概述,如果小伙伴之前对消息队列不是很了解,那么在看理论时会有些困惑,这里以消息模式为切入点,结合理论细节和代码实践的方式一起来学习. 正文 常 ...

  4. Win32简单图形界面程序逆向

    Win32简单图形界面程序逆向 前言 为了了解与学习底层知识,从 汇编开始 -> C语言 -> C++ -> PE文件 ,直至今天的Win32 API,着实学的令我头皮发麻(笑哭). ...

  5. Linux宝塔如何开启指定的目录浏览功能

    哈喽,各位运维晚上好, 今天突发奇想,想给我的个人博客加一个功能,就是如何去打开一个网站文件夹的目录浏览功能,这个还是挺有趣的. 为了以后我还能想起怎么用,我决定记录下来,以便能用,也能给大家一个参考 ...

  6. 如何用AR Engine开发一个虚拟形象表情包?

    现如今,人们在网上聊天.发帖时越来越爱用表情包,表情包一方面是一种个性化的表达方式,另一方面更能传达出当下的心理活动,可以说在网络社交中表情包是一个不可或缺的存在.加上近年来元宇宙的兴起,3D虚拟形象 ...

  7. Logstash集成GaussDB(高斯DB)数据到Elasticsearch

    GaussDB 简介 GaussDB 数据库分为 GaussDB T 和 GaussDB A,分别面向 OLTP 和 OLAP 的业务用户. GaussDB T 数据库是华为公司全自研的分布式数据库, ...

  8. 安装Alertmanager,nginx配置二级路径代理访问

    安装配置 Alertmanager wget https://github.com/prometheus/alertmanager/releases/download/v0.20.0/alertman ...

  9. Kibana管理

    这里是用来管理您的 kibana 运行时配置的地方,包括初始化配置和后续的索引模式配置.高级设置等.您可以调整 kibana 自身的行为,也可以编辑您通过 kibana 保存的查询.视图.仪表板等各种 ...

  10. CentOS使用yum方式安装yarn和nodejs

    # 使用epel-release.repo源安装的nodejs版本是6.17.1,有些前端项目使用的话会提示版本太低,具体下图 # 命令执行后的详细情况:curl -sL https://rpm.no ...