当一个线程在执行过程中抛出了异常,并且没有进行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. 获取进程产生了多少次pagefault

    怎么获取某个进程产生了多少次pagefault? 这个在ps 命令中可以看到,比如查看java的pagefault情况. ps -o maj_flt -o min_flt -p `ps -e|grep ...

  2. 关于Copy On Write Array List,你会安全使用么

    摘要:JDK中提供了CopyOnWriteArrayList类,简称COW.为了将读取的性能发挥到极致,CopyOnWriteArrayList读取是完全不用加锁的,并且更厉害的是:写入也不会阻塞读取 ...

  3. Prometheus教程

    Prometheus介绍 主要特征 使用时间序列数据(tsdb)的多维度数据模型 灵活的查询语言(PromQL) 不依赖分布式存储,单个节点是自主的 通过http请求拉取(pull)时间序列数据(ts ...

  4. sftp服务器的搭建与连接

    在个人租用的vps上搭建sftp服务器并通过本机连接上去 本实验所需:Xshell(xshell中包含xftp).一台vps(windows系统) sftp是一种协议,即SSH File Transf ...

  5. 使用VS Code 搭建 platformio 平台

    一.需要的资源网站 arduino GitHub:https://github.com/arduino espressif GitHub:https://github.com/espressif pl ...

  6. Linux之搭建FTP服务

    引用:FTP服务器(File Transfer Protocol Server)是在互联网上提供文件存储和访问服务的计算机,它们依照FTP协议提供服务. FTP是File Transfer Proto ...

  7. docker_命令总结

    docker -v /hostDir:/containerDir /hostDir为宿主机的目录 /containerDir为容器内的目录 -v 实现两个目录的挂在,即容器内数据持久化到本机 dock ...

  8. java的URI和URL的关系

    java的URI和URL到底是什么 在我们做开发时,经常有URI和URL弄混的问题,如果当时直接看URI和URL的源码就不可能弄混.首先我总结一下URI和URL的关系:他们的关系是:URL是一种特殊的 ...

  9. Vue3 封装 Element Plus Menu 无限级菜单组件

    本文分别使用 SFC(模板方式)和 tsx 方式对 Element Plus el-menu 组件进行二次封装,实现配置化的菜单,有了配置化的菜单,后续便可以根据路由动态渲染菜单. 1 数据结构定义 ...

  10. HashMap的哈希函数为何用(n - 1) & hash

    前言 在上一篇 Java 中HashMap详解(含HashTable, ConcurrentHashMap) 中提到在map.put(key, value)的过程中,计算完key的hash值, 是通过 ...