ThreadPoolExecutor 间歇性地允许未捕获的异常通过主线程
ThreadPoolExecutor intermittently allows uncaught exception through to Main thread
在我为自定义 ThreadPoolExecutor 提供的 Runnable 任务中,我捕获了任何 Throwable。然后我没有抛出相同的异常,而是抛出了一个自定义的 RuntimeException。我在我的执行程序中覆盖了 afterExecute(Runnable runnable, Throwable throwable) 方法,正如预期的那样,该异常在执行后对我可用。据我了解,该异常不应发生任何事情,因为我已经小心处理了它。但是,间歇性地允许异常通过主线程,然后主线程使用 Thread.getUncaughtExceptionHandler() 将其打印到 System.err。我已经通过在我的应用程序的主要方法中放置以下内容来验证这一点:
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("I shouldn't have got here!");
}
});
现在我可以轻松地在上面的代码中什么也不做,并阻止输出到 System.err,但我更想弄清楚为什么 ThreadPoolExecutor 让异常从我给它的任务中逃逸。关于为什么会发生这种情况有什么想法吗?
ThreadPoolExecutor 捕获并重新抛出来自任务的异常:
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
因此异常将由默认处理程序处理。
如果你想避免调用默认处理程序,那么你可以用 FutureTask 包装你的 Runnable,它在内部维护异常,或者创建自己的包装器以便能够在适当的地方处理异常。
如果您使用的 TPE 具有有界队列和 CallerRunsPolicy
拒绝策略,那么 runnable 将在提交线程上执行,没有任何异常包装。
无论如何,从异常堆栈来看,这种情况应该是显而易见的。
在我为自定义 ThreadPoolExecutor 提供的 Runnable 任务中,我捕获了任何 Throwable。然后我没有抛出相同的异常,而是抛出了一个自定义的 RuntimeException。我在我的执行程序中覆盖了 afterExecute(Runnable runnable, Throwable throwable) 方法,正如预期的那样,该异常在执行后对我可用。据我了解,该异常不应发生任何事情,因为我已经小心处理了它。但是,间歇性地允许异常通过主线程,然后主线程使用 Thread.getUncaughtExceptionHandler() 将其打印到 System.err。我已经通过在我的应用程序的主要方法中放置以下内容来验证这一点:
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("I shouldn't have got here!");
}
});
现在我可以轻松地在上面的代码中什么也不做,并阻止输出到 System.err,但我更想弄清楚为什么 ThreadPoolExecutor 让异常从我给它的任务中逃逸。关于为什么会发生这种情况有什么想法吗?
ThreadPoolExecutor 捕获并重新抛出来自任务的异常:
try {
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
因此异常将由默认处理程序处理。
如果你想避免调用默认处理程序,那么你可以用 FutureTask 包装你的 Runnable,它在内部维护异常,或者创建自己的包装器以便能够在适当的地方处理异常。
如果您使用的 TPE 具有有界队列和 CallerRunsPolicy
拒绝策略,那么 runnable 将在提交线程上执行,没有任何异常包装。
无论如何,从异常堆栈来看,这种情况应该是显而易见的。