为什么ExecutorService不调用UncaughtExceptionHandler?

9 浏览
0 Comments

为什么ExecutorService不调用UncaughtExceptionHandler?

我遇到了一个问题,可以总结如下:

当我手动创建线程(即使用java.lang.Thread实例化)时,UncaughtExceptionHandler会被适当调用。然而,当我使用带有ThreadFactoryExecutorService时,处理程序被省略了。我错过了什么吗?

我期望的结果是:三次"Uncaught exception..."的消息。

我得到的结果是:只有一次消息(由手动创建的线程触发)。

在Windows 7和Mac OS X 10.5上使用Java 1.6重现了该问题。

0
0 Comments

根据《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。

原文有误,已修正,谢谢指正。

0
0 Comments

为什么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()才是正确的方法。

0
0 Comments

为什么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,你将会在上面的代码中看到你期望的日志输出。

0