线程池执行UncaughtExceptionHandler失效问题分析

场景

我们知道可以对一个Thread对象设置UncaughtExceptionHandler来进行自定义的未捕捉异常处理。具体可参考上一篇文章Thread自定义异常处理

但是,如果交由线程池来执行的话,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void main(String[] args) {
Thread task = new Thread(() -> {
System.out.println("Before...");
System.out.println(10 / 0);
System.out.println("After...");
});
task.setName("Thread-demo");
task.setUncaughtExceptionHandler((t1, e) -> {
System.out.println("自定义异常: " + t1);
System.out.println(e);
});
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(task);
exec.shutdown();
}

从代码可以看到,线程task设置了UncaughtExceptionHandler,并交给线程池来执行,结果:
image.png

可以看到,结果并不像我们设想的那样,居然没有走我们自己定义的异常处理的方法。

原因

为什么UncaughtExceptionHandler会失效呢,冷静下来仔细回想一下。从上一篇文章的分析中,我们知道只有为线程设置了UncaughtExceptionHandler,该线程在发生异常时,才会执行对应的uncaughtException方法。

如此说来,提交给线程池执行的时候,有可能实际运行task的线程并不是task线程本身(这可能有点拗口)。如果不是task线程本身,又是谁在执行task呢?于是决定对两种方式进行debug,

1、 线程池执行
image.png

很明显,当前运行task的是线程池创建的线程”pool-1-thread-1”,而我们只是为线程”Thread-demo”设置了uncaughtExceptionHandler,所以就解释了为什么没有走自定义的处理了。

2、单线程执行
把上面的例子改为task.start()执行,再debug
image.png

可以看到,这一次就是task线程本身也就是”Thread-demo”在执行了,而uncaughtExceptionHandler属性也就不为null。

结论

1、如果让线程池执行的同时,也能走自定义uncaughtExceptionHandler,可以在task的开头加上

1
2
3
4
5
6
7
8
9
10
Thread task = new Thread(() -> {
Thread.currentThread(). setUncaughtExceptionHandler((t1, e) -> {
System.out.println("自定义异常: " + t1);
System.out.println(e);
});
System.out.println("Before...");
System.out.println(10 / 0);
System.out.println("After...");
});

多线程