UncaughtExceptionHandler 获取线程运行时异常

2023-11-11

我们知道线程执行体的方法接口定义为: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>全局

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UncaughtExceptionHandler 获取线程运行时异常 的相关文章

  • futureTask RunnableFuture Future 三者关系认知

    对于这三者首先我们看下源码 之后在分别写几个demo讲解下用法 public interface RunnableFuture
  • Thread.UncaughtExceptionHandler

    1 自定义Application继承Application 在清单文件中将默认的Application的android name 替换成自定义的Application对象名称即可 在Oncreate中写入要实现的内容 2 在编写APK程序时
  • fcgi程序两种编写风格

    fcgi进程可以写成单线程的 也可以写成多线程的 单线程就是main函数中有一个死循环 一直等待接受请求 有请求过来时 就处理请求 并返回结果 没有并发性 多线程也分两种模式 一种是main函数起多个线程 每个线程都独立接受请求 另一种是m
  • Kafka 顺序消费方案

    Kafka 顺序消费方案 前言 1 问题引入 2 解决思路 3 实现方案 前言 本文针对解决Kafka不同Topic之间存在一定的数据关联时的顺序消费问题 如存在Topic insert和Topic update分别是对数据的插入和更新 当
  • 多线程实现事务回滚

    多线程实现事务回滚 特别说明CountDownLatch CountDownLatch的用法 CountDownLatch num 简单说明 主线程 mainThreadLatch await 和mainThreadLatch countD
  • 多线程太可怕了

    今天发现了一个多线程引起的bug 然后进一步体会到 这东西太容易出问题了 首先要说明的是 出问题的代码可不是一般人写的 是由一个叫EPAM systems的世界知名外包公司的人写的 这些java程序员个个经验丰富 心高气傲 貌似base在乌
  • java使用线程池批量插入mysql数据

    首先我们使用最原始的for循环插入数据 for int i 0 i lt 100000000 i service add new LongTest setStatus 1 setName NumberUtil getPwdRandom 5
  • 关于Semaphore信号量的源码解读

    Semaphore的简单使用 利用Semaphore可以实现对线程数量的控制 比如如下的代码 class SemaphoreTest public static void main String args Semaphore semapho
  • 线程封闭概念

    线程封闭概念 为什么要有线程封闭这个概念呢 多线程中访问共享可变数据时 涉及到线程间数据同步的问题 并不是所有时候都需要共享数据 所以线程封闭概念就出来了 在Java中线程封闭该怎么做呢 可以通过两个方法来做 ThreadLocal 1 T
  • 条件变量(condition variable)详解

    原理 假设我们需要解决这样一个问题 一个列表记录需要处理的任务 一个线程往此列表添加任务 一个线程processTask处理此列表中的任务 这个问题的一个关键点在于processTask怎么判断任务列表不为空 一般有两种方法 一 proce
  • Java 多线程模式 —— Guarded Suspension 模式

    Part1Guarded Suspension 模式的介绍 我们只从字面上看 Guarded Suspension 是受保护暂停的意思 1Guarded Suspension 模式 在实际的并发编程中 Guarded Suspension
  • MFC多线程编程之一——问题提出

    原文地址 http www vckbase com document viewdoc id 1704 一 问题的提出 编写一个耗时的单线程程序 新建一个基于对话框的应用程序SingleThread 在主对话框IDD SINGLETHREAD
  • C++多线程(并发、进程、线程的基本概念和综述)

    并发 进程 线程的基本概念和综述 并发 并发表示两个或者更多任务 独立的活动 同时发生 进行 例如 一面唱歌一面弹琴 一面走路一面说话 画画的时候听小说等 回归到计算机领域 所谓并发 就是一个程序同时执行多个独立的任务 以往计算机只有单核C
  • Java多线程 - 线程池常用容量设置

    线程执行方式 线程的执行是由CPU进行调度的 一个CPU在 同一时刻只会执行一个线程 操作系统利用了时间片轮转的方式 CPU给每个任务都服务一定的时间 然后把当前任务的状态保存下来 再加载下一个任务的状态后 继续服务下一个任务 任务的保存及
  • wxwidgets编写多线程程序--wxThread

    细节描述 线程基本上来说是应用程序中一条单独执行的路径 线程有时被称为轻量级进程 但线程与进程的根本不同之处在于不同进程存储空间是相互独立的 而同一进程里的所有线程共享同一地址空间 尽管这使得它更容易共享几个线程间的普通数据 但这也使得它有
  • JAVA并发:线程安全与Synchorinzed

    1 什么是线程安全问题 线程的合理使用能够提升程序的处理性能 主要有两个方面 第一个是能够利用多核 cpu 以及超线程技术来实现线程的并行执行 第二个是线程的异步化执行相比于同步执行来说 异步执行能够很好的优化程序的处理性能提升并发吞吐量
  • CentOS 7 调优之周期性的访问中断

    文章目录 背景 问题描述 原因分析 解决方案 相关版本 背景 操作系统版本 CentOS Linux release 7 6 1810 Core 操作系统镜像安装后 未进行任何调整 正常部署应用 应用在 CentOS 7 9 未出现过此类现
  • VS2008编译的程序在某些机器上运行提示“由于应用程序配置不正确,应用程序未能启动”的问题...

    VC9编译的程序在没有装过VC9 确切的说是 Net Framework3 5 的机器上运行时 如果提示 由于应用程序配置不正确 应用程序未能启动 重新安装应用程序可能会纠正这个问题 这个错误 那么就说明该程序动态链接了VC9的运行时库 如
  • 多线程编程与性能优化

    引言 在上一篇的入门篇中 我们对Android线程的基础概念和多线程编程模型有了初步了解 本篇将深入探讨多线程编程技术和性能优化策略 以提升应用的效率和响应性 高级多线程编程技术 使用线程池管理线程 线程池是一组预先创建的线程 用于执行任务
  • TaskDecatator用法

    在Spring框架中 TaskDecorator 是一个接口 它可以用来自定义由 ThreadPoolTaskExecutor 或其他任务执行器管理的任务的装饰行为 这通常用于在执行任务之前和之后添加某些上下文相关的行为 比如设置线程上下文

随机推荐

  • promise函数几种写法与坑

    promise是ES6中引入的处理异步函数的强大特性 但是对promise的不恰当使用可能会达不到最终目的 对这个问题的探究来源于这篇文章关于promises 你理解了多少 几个异步函数如下 resolve或reject在回调函数里被调用
  • 网络编程的几种I/O模式

    1 非阻塞I O 非阻塞I O 若想网络编程时调用I O函数不想让程序阻塞 需要使用I O复用技术 一个方法是poll 轮询 所谓轮询就是执行函数时 如果内核不能立即对应用的函数进行响应时 就返回给应用一个错误 而应用不停的循环调用该函数
  • JavaScript表示不背小数计算存在误差的锅

    浮点数的最高精度是17位小数 但是在实际计算时会产生莫名其妙的问题 如0 1 0 2的结果不是0 3 而是0 30000000000000004 这个舍入误差会导致无法测试特定的浮点数值 例如 var a 0 1 b 0 2 if a b
  • 【数据结构】采用邻接矩阵表示法创建无向网、无向图、有向图、有向网

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 目录 一 无向网 权值 对称 1 思路 2 代码 3 运行结果 三 其他 1 无向图 0 1 对称 2 有向网 权值 不对称 3 有向图 0 1 不对称 一 无向网 1 思路
  • 使用python的pandas包查询数据库数据导出到excel

    文章目录 前言 1 实现分析 2 实现过程 2 1安装环境 2 3功能逻辑 2 4完整代码 3 总结 前言 前几天接到一个业务的需求 让我把当前数据库里面的结果数据导出到excel中 然后供业务查看 问题是当前结果数据都是列式表 所以需要把
  • 强化学习 reward 曲线的绘制

    每隔一段取一个均值 然后把均值曲线绘制出来 包含全部点的曲线淡化处理 摘自 Z Mou Y Zhang F Gao H Wang T Zhang and Z Han Deep Reinforcement Learning based Thr
  • paddlepaddle安装问题protobuf package to 3.20.x or lower.

    按照官方文档安装paddlepaddle 2 3 0后 进行环境验证时 总是提示如下错误 TypeError Descriptors cannot not be created directly If this call came from
  • 【编程测试题】数字游戏

    题目描述 小易邀请你玩一个数字游戏 小易给你一系列的整数 你们俩使用这些整数玩游戏 每次小易会任意说一个数字出来 然后你需要从这一系列数字中选取一部分出来让它们的和等于小易所说的数字 例如 如果 2 1 2 7 是你有的一系列数 小易说的数
  • 【删除列表中最后一条数据遇到的问题】

    项目场景 列表有多页数据 如果最后一页只有一条数据 当删除这条数据时 页码能够改变并且数据正确展示 问题描述 当删除这条数据时 页码可以正确展示 但是数据没有正确显示 handleDelete row const id row userId
  • 服务器 虚拟san,关于服务器 SAN 和 SDS

    注明 本文内容基于VMware VSAN beta版本撰写 请访问http www vmware com products virtual san 获得有关正式版本的更新信息 我很高兴在Wikibon上读到了我的同事Stu Miniman
  • 【微信小程序】项目初始化

    var CSS 函数可以插入一个自定义属性 有时也被称为 CSS 变量 的值 用来代替非自定义 属性中值的任何部分 1 初始化样式与颜色 view text box sizing border box page themColor ad90
  • linux---配置bond方法

    配置bond方法 原始配置文件1 DEVICE eth0 BOOTPROTO dhcp HWADDR 00 0C 29 04 AE 65 IPV6INIT no NM CONTROLLED no ONBOOT yes TYPE Ethern
  • 不用插拔网线鼠标点击自动切换网线和WIFI

    因为之前在zf单位工作 政务内网需要插网线 而访问外网又需要连wifi 切换就需要拔掉网线插上网线很麻烦 旁边老哥教我了一手 bat程序自动切换方法 bat文件代码如下 以下代码的 bat文件执行后会切换到以太网 同时关闭掉wifi和以太网
  • 监听对象中属性变化(一个或多个属性、全部属性)

    一 数据监听器 什么是数据监听器 数据监听器用于监听和响应任何属性和数据自动的变化 从而执行特定的操作 它的作用类似于vue中的watch侦听器 在小程序中 基本语法格式如下 Component observers 字段A 字段B func
  • 积分规划:构建全面会员积分管理系统

    在当今竞争激烈的市场环境中 企业要想保持用户的忠诚度和活跃度 建立一个全面的会员积分管理系统是至关重要的 积分制度不仅可以激励用户参与 还可以增加用户的消费频次和购买金额 本文将深入探讨如何构建全面的会员积分管理系统 以实现更好的私域营销效
  • 消息队列(MQ)

    一 为什么要用消息队列 消息队列的应用场景 应用解耦 异步任务 流量削峰 问题背景 学生向老师请教问题 如果学生A正在向老师请教问题 那么后面的学生依次排队等候 直到轮到自己请教问题 这样的模式会使整个系统的效率较低 学生排队等待时间太久
  • pygame学习笔记

    pygame学习笔记 1 基础知识 参考 1 基础知识 1 设置死循环 持续显示窗口 import pygame import sys pygame init 初始化pygame size 320 240 设置窗口大小 w h screen
  • 数字逻辑触发器(一)

    触发器 一 定义 是一种具有记忆功能的逻辑部件 具有两个稳定的输出状态 用这两个稳定的状态来表示二值信号的0和1 在外界输入信号的激励下 触发器的输出状态会发生改变 二 触发器的种类举例 基本RS触发器 主从触发器 维持阻塞触发器 D触发器
  • 【appium报错】Original error:Could not proxy command to remote server. Original error:socket hang up

    博客链接1 系统自带的应用kill掉appium相关的进程 博客链接2 卸载并重装appium相关安装包 如果上面的方法仍不能解决问题 暴力解决 卸载设备上的appium setting 等appium安装的app 卸载自动化启动的app
  • UncaughtExceptionHandler 获取线程运行时异常

    我们知道线程执行体的方法接口定义为 public void run 因此线程在执行单元中是不允许抛出checked异常的 且线程之间是相对独立的 他们运行在自己的上下文当中 派生它的线程无法直接感知到它在运行时出现的异常信息 为了解决这个问