为什么ExecutorService不调用UncaughtExceptionHandler?
根据《Java并发编程实践》书中所述(第163页),通过execute方法提交的任务抛出的异常只会通过未捕获异常处理器处理;而通过submit方法提交的任务,不论是检查异常还是非检查异常,都被视为任务的返回状态的一部分。如果通过submit方法提交的任务由于异常而终止,会在Future.get方法中被重新抛出,包装在ExecutionException中。
下面是示例代码:
public class Main { public static void main(String[] args){ ThreadFactory factory = new ThreadFactory(){ public Thread newThread(Runnable r) { // TODO Auto-generated method stub final Thread thread =new Thread(r); thread.setUncaughtExceptionHandler( new Thread.UncaughtExceptionHandler() { public void uncaughtException(Thread t, Throwable e) { // TODO Auto-generated method stub System.out.println("in exception handler"); } }); return thread; } }; ExecutorService pool=Executors.newSingleThreadExecutor(factory); pool.execute(new TestTask()); } private static class TestTask implements Runnable { public void run() { // TODO Auto-generated method stub throw new RuntimeException(); } }
在这段代码中,我使用execute方法来提交任务,控制台输出了"in exception handler"。
建议将类testTask的名称改为TestTask。
原文有误,已修正,谢谢指正。
为什么ExecutorService不会调用UncaughtExceptionHandler的原因以及解决方法
在使用ExecutorService的submit方法提交任务时,抛出的异常会被包装成ExecutionException,并通过Future.get()方法重新抛出。这是因为执行器将异常视为任务结果的一部分。
然而,如果您使用Executor接口中的execute方法提交任务,UncaughtExceptionHandler将会被通知。
这是一个令人惊讶的简洁而易用的解决方案。非常感谢!
到目前为止,这是最简单的解决方案。关于这种行为的更多信息,请参考Stack Overflow上的这个回答:stackoverflow.com/a/3986509/2874005
在我的实验中,如果ExecutorService已经被关闭,并且其最后一个存活的工作线程遇到了未捕获的异常,它似乎会在调用处理程序之前终止。我无法从ThreadPoolExecutor中看出为什么会这样。
感谢您指出submit()和execute()之间的异常处理在本质上有所不同。当你仔细考虑时,这是合理的,但乍一看确实令人惊讶。由于execute()方法没有在ExecutorService中定义,而是在其超级接口Executor中定义的,所以在浏览JavaDoc时很容易忽略它。可能有很多情况下使用submit(),但是execute()才是正确的方法。
为什么ExecutorService不调用UncaughtExceptionHandler的原因以及解决方法
在ExecutorService中,UncaughtExceptionHandler没有被调用的原因是因为异常并没有被捕获。ThreadFactory生成的Thread并没有直接执行你的Runnable或者Callable,而是通过一个内部的Worker类来执行。你可以尝试在你的示例中给newThread方法中的Runnable加上System.out.println()来查看。Worker类会捕获你提交的任务中的任何RuntimeException。
你可以在ThreadPoolExecutor的afterExecute方法中获取到异常。虽然这个答案是正确的,但是正确实现afterExecute方法是比较棘手的。你可以参考Stack Overflow上的一个问题来了解如何正确实现这个解决方法。
这篇文章介绍了在多线程代码中处理异常的方法,包括Future和ExecutorService的使用。
这个答案并不完全正确。UncaughtExceptionHandler无法按预期工作的原因并不是因为内部的Worker类吞掉了异常。而是因为使用了submit方法,这意味着会创建一个Future对象并在将来执行任务。除了实现更复杂的afterExecute方法之外,你还可以简单地使用submit方法返回的Future对象,并通过whenComplete方法来处理异常。如果你使用threadPool.execute(runnable)而不是submit,你将会在上面的代码中看到你期望的日志输出。