多线程的未捕获异常类 UncaughtExceptionHandler 的使用
一、需要 UncaughtExceptionHandler 的原因
1. 主线程可轻松的发现异常,子线程的异常比较隐蔽,难以发现
程序运行时,子线程发生了异常,并不影响主线程,也不会终止主线程的程序,主线程将继续执行,这时候子线程的异常可能就不会被察觉,就使得子线程的功能出了问题,但没发现。
代码展示:
/**
* 单线程时,抛出异常,很容易被发现,然后我们就可以处理异常堆栈
* 多线程,子线程发生异常,会有什么不同???
* 子线程发生异常时,不影响主线程,不会终止主线程的程序,主线程将继续执行,子线程的异常信息可能被主线程的日志输出给覆盖掉
*/
public class ExceptionInChildThread implements Runnable {
@Override
public void run() {
throw new RuntimeException();
}
//主线程
public static void main(String[] args) {
new Thread(new ExceptionInChildThread()).start();
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
}
}
打印结果:

如图所示,当主线程输出大量信息时,子线程的异常信息就被淹没了。而且子线程的异常不会影响到主线程的运行,所以很难被发现。
2. 子线程异常无法用传统异常捕获方式捕获
(1)当没有捕获子线程的异常时
public class CantCatchDirectly implements Runnable {
@Override
public void run() {
throw new RuntimeException();
}
public static void main(String[] args) throws InterruptedException {
new Thread(new CantCatchDirectly(),"线程1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程4").start();
}
}
打印结果:

可以看到四个子线程实际都抛出异常了,然后接下来我们看看使用 try-catch 尝试捕获异常。
(2)使用 try-catch 捕获子线程的异常
加上 try-catch,期望捕获第一个子线程的异常。如果捕获成功的话会直接进入到 catch 代码块中,余下的子线程234应该不会运行,控制台中应该也只会有第一个子线程的异常信息。
public class CantCatchDirectly implements Runnable {
@Override
public void run() {
throw new RuntimeException();
}
public static void main(String[] args) throws InterruptedException {
try {
new Thread(new CantCatchDirectly(),"线程1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程4").start();
} catch (RuntimeException e) {
System.out.println("抓住异常");
e.printStackTrace();
}
}
}
打印结果

结果发现,异常根本就没捕获到,这是为什么呢?
因为 try-catch 只能捕获到当前线程内的异常,执行 try-catch 的是主线程,而异常是发生在子线程中,无法进行捕获;
(3)子线程异常不能捕获的后果与改进
后果:子线程出现异常却没人处理,就会导致子线程的那部分逻辑无法成功执行,可能就会影响到程序的整体功能实现。
改进:如果能对子线程的异常及时发现,我们就可以重启线程,或者直接报警去通知运维人员,从而人工处理该异常,等等这类的补救措施,都会对程序的稳定运行很有利,从而提高代码的健壮性,这时我们就需要用到这个能对未捕获异常进行处理的类 UncaughtExceptionHandler。
二、未捕获异常的解决方案
在使用 UncaughtExceptionHandler 解决未捕获异常之前,我们先尝试另一种解决方案,但是不推荐,只是作为参考,那就是在每个子线程可能出现异常的地方都加上 try-catch
1. 手动在每个run方法里进行try catch (不推荐)
public class CantCatchDirectly implements Runnable {
@Override
public void run() {
try {
throw new RuntimeException();
} catch (RuntimeException e) {
System.out.println("捕获异常");
}
}
public static void main(String[] args) throws InterruptedException {
new Thread(new CantCatchDirectly(),"线程1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程4").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(),"线程5").start();
}
}
打印结果:

四个线程都不捕获到了异常,该方法确实能处理每一个子线程的异常,但是这样费时耗力,而且也不一定能把所有的异常点全都考虑到。
2. 利用 UncaughtExceptionHandler (推荐)
在 UncaughtExceptionHandler 接口中有且仅有一个方法 void uncaughtException(Thread t,Throwable e);

异常处理器的调用策略

默认情况下,异常处理器中是没有 handler,所以会直接打印出异常堆栈信息,所以我们需要自己设置一个全局 handler,也就是自己 实现一个 UncaughtExceptionHandler 。
(1)自定义 UncaughtExceptionHanlder
/**
* 自定义 UncaughtExceptionHandler
*/
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler{
private String name;
public MyUncaughtExceptionHandler(String name) {
this.name = name;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.WARNING,"线程异常:"+t.getName(),e);
System.out.println(name+"我捕获了异常"+t.getName()+"异常名字:"+e);
}
}
(2) 使用自定义 UncaughtExceptionHandler
也就是将我们自己实现的 MyUncaughtExceptionHandler 放进 DefaultUncaughtExceptionHandler 中,这样线程在遇到未捕获异常时,会优先执行该类中的逻辑。
/**
* 使用自定义的 UncaughtExceptionHandler
*/
public class UseMyUncaughtExceptionHandler implements Runnable {
@Override
public void run() {
throw new RuntimeException();
}
public static void main(String[] args) throws InterruptedException {
//设置自定义异常处理器
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("我们自定义的线程异常处理器"));
new Thread(new UseMyUncaughtExceptionHandler(),"线程1").start();
Thread.sleep(300);
new Thread(new UseMyUncaughtExceptionHandler(),"线程2").start();
Thread.sleep(300);
new Thread(new UseMyUncaughtExceptionHandler(),"线程3").start();
Thread.sleep(300);
new Thread(new UseMyUncaughtExceptionHandler(),"线程4").start();
Thread.sleep(300);
new Thread(new UseMyUncaughtExceptionHandler(),"线程5").start();
}
}
打印结果

可以看到在遇到子线程中的未捕获异常时,就会执行 MyUncaughtExceptionHandler 类中的逻辑,这样如果想把异常通知给告警系统或者封装成一个提示语返回给前端,都可以在这个类中进行实现。
三、课后测验
1. 如何处理全局异常?为什么要全局异常处理?不处理行不行?
通过实现一个全局的UncaughtExceptionHandler接口,自定义一个全局处理器;
打印日志方便我们后期的维护,也可以实现返回给前端的统一提示信息;
不处理不行,不处理的话程序会抛出异常子线程中断,主线程不影响,会继续运行。
2. run方法是否可以抛出异常?如果抛出异常,线程的状态会怎么样?
run方法是已经声明好的,固定的,没有声明异常,便不能够向外抛出异常了,只能自己处理try-catch;
如果run方法内部 throws RuntimeException,又没有进行 try-catch 捕获,那么程序会抛出异常,终止运行,打印出异常堆栈。
3. 线程中如何处理某个未处理异常?
通过实现一个全局的UncaughtExceptionHandler接口;
文章来源:多线程的未捕获异常类 UncaughtExceptionHandler 的使用
个人微信:CaiBaoDeCai
微信公众号名称:Java知者
微信公众号 ID: JavaZhiZhe
谢谢关注!
多线程的未捕获异常类 UncaughtExceptionHandler 的使用的更多相关文章
- APP级别处理未捕获异常
前言: 项目APP有时候会出现Crash,然后就是弹出系统强制退出的对话框,点击关闭APP. 有的APP进行了处理,会发现,当程序出现异常的时候,会Toast一个提示"程序出现异常,3秒后将 ...
- 关于未捕获异常的处理(WPF)
这一篇文章来谈谈对于WPF应用程序开发中的未捕获异常的处理. 首先,我们当然是要求应用程序开发人员,尽可能地在程序可能出现异常的地方都去捕捉异常,使用try-catch的方式.但是总是有一些意外的情况 ...
- WPF 之 未捕获异常的处理
首先,我们当然是要求应用程序开发人员,尽可能地在程序可能出现异常的地方都去捕捉异常,使用try…catch的方式.但是总是有一些意外的情况可能会发生,这就导致会出现所谓的“未捕获异常(Unhandle ...
- 【转】Winform程序未捕获异常解决方法 EventType clr20r3 P1
from:http://blog.csdn.net/chichaodechao/article/details/8294922 在开发winform程序时,用到多线程,在服务器部署后运行,老是自动关才 ...
- Java线程未捕获异常处理 UncaughtExceptionHandler
当一个线程在执行过程中抛出了异常,并且没有进行try..catch,那么这个线程就会终止运行.在Thread类中,提供了两个可以设置线程未捕获异常的全局处理器,我们可以在处理器里做一些工作,例如将异常 ...
- .Net 下未捕获异常的处理
原地址:http://www.cnblogs.com/eaglet/archive/2009/02/17/1392191.html .Net 下未捕获异常的处理 作者:Eaglet 随着.Net技术的 ...
- Java多线程之线程其他类
Java多线程之线程其他类 实际编码中除了前面讲到的常用的类之外,还有几个其他类也有可能用得到,这里来统一整理一下: 1,Callable接口和Future接口 JDK1.5以后提供了上面这2个接口, ...
- MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 转
MFC关于多线程中传递窗口类指针时ASSERT_VALID出错的另类解决 在多线程设计中,许多人为了省事,会将对话框类或其它类的指针传给工作线程,而在工作线程中调用该类的成员函数或成员变量等等. ...
- “全栈2019”Java多线程第二章:创建多线程之继承Thread类
难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...
- Android-小小设置永久解决程序因为未捕获异常而异常终止的问题
(一) 前言各位亲爱的午饭童鞋,是不是经常因为自己的程序中出现未层捕获的异常导致程序异常终止而痛苦不已?嗯,是的.. 但是,大家不要怕,今天给大家分享一个东东可以解决大家这种困扰,吼吼! (二) Un ...
随机推荐
- ARP协议:网络世界的临门一脚
大家好,我是风筝. 各位同学肯定见过关于网络的面试题,什么TCP协议和UDP的区别啦,IP协议工作在哪层啊等等,这都是网络中定义的各种协议.这些标准化的协议就是网络分层模型标准化的核心部分.要想搞懂网 ...
- 基于深度学习的鸟类检测识别系统(含UI界面,Python代码)
摘要:鸟类识别是深度学习和机器视觉领域的一个热门应用,本文详细介绍基于YOLOv5的鸟类检测识别系统,在介绍算法原理的同时,给出Python的实现代码以及PyQt的UI界面.在界面中可以选择各种鸟类图 ...
- 集合-LinkedList 源码分析(JDK 1.8)
1.概述 LinkedList 是 Java 集合框架中一个重要的实现,其底层采用的双向链表结构.和 ArrayList 一样,LinkedList 也支持空值和重复值.由于 LinkedList 基 ...
- 修复kube-proxy证书权限过大问题
修复kube-proxy证书权限过大问题 之前kube-proxy服务都是用admin集群证书,造成权限过大不安全,后续该问题,将在文档中修复 请关注 https://github.com/cby-c ...
- TOP使用参数
TOP使用参数top是检查机器当前运行状况的第一个命令,就好比是机器体检时的第一张报告单.先了解一下TOP命令的使用 [root@localhost /]# top -help top: procps ...
- GitHub+Hexo 搭建博客网站
Hexo是一款基于Node.js的静态博客框架,依赖少易于安装使用,可以方便的生成静态网页托管在GitHub和Heroku上,是搭建博客的首选框架. 配置Github root@hello:~/cby ...
- [软件测试]Web接口的性能测试
1 接口响应性能影响因素分析 影响Web接口查询响应性能的重要因素: 1.网络/带宽.服务器硬件资源(CPU.内存.磁盘) 2.用户并发数 3.查询的基础数据集的量级.百万级?亿级?百亿级? 4.查询 ...
- 【Spring注解驱动】(三)Servlet 3.0
前言 今天是7.21日,终于是看完了..暑假在家学习是真的差点意思 1 Servlet 3.0简介 Servlet 2.0是在web.xml中配置servlet filter.listener.Dis ...
- STM32启动分析之main函数是怎样跑起来的
1.MDK目标文件 1)MDK中C程序编译后的结果,即可执行文件数据分类: RAM ZI bss 存储未初始化的或初始化为0的全局变量和静态变量 heap 堆,系统malloc和free操作的内存 s ...
- 笔记二:进程间的通信(fork、孤儿进程,僵死进程等)
以下是以前学习<unix环境高级编程>时的一些笔记和测试代码,好久没看过了,没有再次验证,存在错误的话,希望见谅,分享下主要是!!! ps 查看系统中的进程 ps– ...