开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
学习MOOC视频记录的笔记
Java异常体系图
实际工作中,如何全局处理异常?为什么要全局处理?不处理行不行?
1.线程的未捕获异常 UncaughtException 应该如何处理?
1.1 为什么需要 UncaughtExceptionHandler?
- 主线程可以轻松发现异常,子线程却不行
public class ExceptionInChildThread implements Runnable {
public static void main(String[] args) {
new Thread(new ExceptionInChildThread()).start();
for (int i = 0; i < 1000; i++) {
System.out.println("i = " + i);
}
}
@Override
public void run() {
throw new RuntimeException();
}
}
子线程抛出异常,主线程的运行丝毫不受影响。
i = 0
i = 1
Exception in thread "Thread-0" java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.ExceptionInChildThread.run(ExceptionInChildThread.java:18)
at java.lang.Thread.run(Thread.java:748)
i = 2
i = 3
- 子线程异常无法用传统方法捕获
/**
* 1. 不加 try catch 抛出4个异常,都带线程名字
* 2. 加了 try catch 期望捕获到第一个线程的异常,线程234不应该运行,希望看到打印出Caught Exception
* 3. 执行时发现,根本没有 Caught Exception,线程234依然运行并且抛出异常
*
* 说明线程的异常不能用传统方法捕获
*/
public class CantCatchDirectly implements Runnable {
public static void main(String[] args) throws InterruptedException {
new Thread(new CantCatchDirectly(), "MyThread-1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-4").start();
}
@Override
public void run() {
throw new RuntimeException();
}
}
直接抛出异常:
Exception in thread "MyThread-1" java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:23)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "MyThread-2" java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:23)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "MyThread-3" java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:23)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "MyThread-4" java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:23)
尝试使用 try-catch 来捕获异常:
public class CantCatchDirectly implements Runnable {
public static void main(String[] args) throws InterruptedException {
try {
new Thread(new CantCatchDirectly(), "MyThread-1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-4").start();
} catch (InterruptedException e) {
System.out.println("Caught Exception");
}
}
@Override
public void run() {
throw new RuntimeException();
}
}
运行结果却没有改变:
Exception in thread "MyThread-1" java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "MyThread-2" java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "MyThread-3" java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "MyThread-4" java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
出现问题的原因是:抛出异常是在子线程中抛出的,但是使用try-catch捕获异常却是在主线程中进行的,因此捕获失效了。
- 不能直接捕获的后果、提高健壮性
1.2 两种解决方案
方案一(不推荐):手动在每个 run 方法里进行 try catch
public class CantCatchDirectly implements Runnable {
public static void main(String[] args) throws InterruptedException {
new Thread(new CantCatchDirectly(), "MyThread-1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "MyThread-4").start();
}
@Override
public void run() {
try {
throw new RuntimeException();
} catch (RuntimeException e) {
System.out.println("Caught Exception");
}
}
}
由输出可见异常被捕获了:
Caught Exception
Caught Exception
Caught Exception
Caught Exception
方案二(推荐):利用 UncaughtExceptionHandler
该接口中只有一个方法:void uncaughtException(Thread t,Throwable e);
异常处理器的调用策略
默认异常处理器首先检查有没有父线程,一直找到最顶层的异常处理器
自己实现
- 给程序统一设置
- 给每个线程单独设置
- 给线程池设置
异常捕获器
/**
* 自己的MyUncaughtExceptionHandler
*/
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);
}
}
捕获子线程异常
public class UseOwnUncaughtExceptionHandler implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("捕获器1"));
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-1").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-2").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-3").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "MyThread-4").start();
}
@Override
public void run() {
throw new RuntimeException();
}
}
日志:
十一月 10, 2022 1:31:07 上午 threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦MyThread-1
java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:18)
at java.lang.Thread.run(Thread.java:748)
捕获器1捕获了异常MyThread-1异常java.lang.RuntimeException
十一月 10, 2022 1:31:08 上午 threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦MyThread-2
java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:18)
at java.lang.Thread.run(Thread.java:748)
捕获器1捕获了异常MyThread-2异常java.lang.RuntimeException
捕获器1捕获了异常MyThread-3异常java.lang.RuntimeException
十一月 10, 2022 1:31:08 上午 threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦MyThread-3
java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:18)
at java.lang.Thread.run(Thread.java:748)
十一月 10, 2022 1:31:08 上午 threadcoreknowledge.uncaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦MyThread-4
java.lang.RuntimeException
at threadcoreknowledge.uncaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:18)
at java.lang.Thread.run(Thread.java:748)
捕获器1捕获了异常MyThread-4异常java.lang.RuntimeException
2.线程的未捕获异常–
常见面试问题
-
Java异常体系
-
如何全局处理异常?为什么要全局处理?不处理行不行?
使用全局的 UncaughtExceptionHandler,前端处理以及后端日志保存
- run方法是否可以抛出异常?如果抛出异常,线程的状态会怎么样?
方法层面不能向外抛,只能自己处理。如果抛出异常,线程会终止运行,打印异常堆栈
- 线程中如何处理某个未处理异常?
使用全局的 UncaughtExceptionHandler。