前言
启动一个Java程序,本质上是运行某个Java类的main方法。我们写一个死循环程序,跑起来,然后运行 jvisualvm 进行观察

可以看到这个Java进程中,一共有11个线程,其中10个守护线程,1个用户线程。我们main方法中的代码,就跑在一个名为 main 的线程中。 当Java进程中跑着的所有线程都是守护线程时,JVM就会退出 。
在单线程的场景下,如果代码运行到某个位置时抛出了异常,会看到控制台打印出异常的堆栈信息。
但在多线程的场景下,子线程中发生的异常,不一定就能及时的将异常信息打印出来。
我曾经在工作中遇到过一次,采用 CompletableFuture.runAsync 异步处理耗时任务时,任务处理过程中出现异常,然而日志中没有任何关于异常的信息。
时隔许久,重新温习了线程中的异常处理机制,加深了对线程工作原理的理解,特此记录。
线程的异常处理机制
我们知道,Java程序的运行,是先经由 javac 将Java源代码编译成class字节码文件,然后由JVM加载并解析class文件,随后从主类的main方法开始执行。
当一个线程在运行过程中抛出了 未捕获异常 时,会由JVM调用这个线程对象上的 dispatchUncaughtException 方法,进行异常处理。
// Thread类中
private void dispatchUncaughtException(Throwable e) {
getUncaughtExceptionHandler().uncaughtException(this, e);
}
源码很好理解,先获取一个 UncaughtExceptionHandler 异常处理器,然后通过调用这个异常处理器的 uncaughtException 方法来对异常进行处理。(下文用缩写 ueh 来表示 UncaughtExceptionHandler )
ueh 是个 啥呢?其实就是定义在 Thread 内部的一个接口,用作异常处理。
@FunctionalInterface
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given thread terminates due to the
* given uncaught exception.
* <p>Any exception thrown by this method will be ignored by the
* Java Virtual Machine.
* @param t the thread
* @param e the exception
*/
void uncaughtException(Thread t, Throwable e);
}
再来看下 Thread 对象中的 getUncaughtExceptionHandler 方法
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler != null ?
uncaughtExceptionHandler : group;
}
先查看当前这个 Thread 对象是否有设置自定义的 ueh 对象,若有,则由其对异常进行处理,否则,由当前 Thread 对象所属的线程组( ThreadGroup )进行异常处理。我们点开源码,容易发现 ThreadGroup 类本身实现了 Thread.UncaughtExceptionHandler 接口,也就是说 ThreadGroup 本身就是个异常处理器。
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
private final ThreadGroup parent;
....
}
假设我们在 main 方法中抛出一个异常,若没有对 main 线程设置自定义的 ueh 对象,则交由 main 线程所属的 ThreadGroup 来处理异常。我们看下 ThreadGroup 是怎么处理异常的:
public void uncaughtException(Thread t, Throwable e) {
if (parent != null) {
parent.uncaughtException(t, e);
} else {
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
这部分源码也比较简短。首先是查看当前 ThreadGroup 是否拥有父级的 T

本文探讨了Java线程的异常处理机制,包括线程如何处理未捕获异常,异常如何通过线程的默认异常处理器输出到控制台。在线程池场景下,通过ExecutorService的submit方法提交的任务,异常会被保存在Future对象中,不会立即打印,需要通过Future.get方法获取异常信息。此外,文章还分析了线程池中不同提交任务方式的异常处理区别。
最低0.47元/天 解锁文章
2506

被折叠的 条评论
为什么被折叠?



