处理 Java 执行器服务任务中的异常

我正在尝试使用Java的类来运行具有固定线程数量的大量重型任务。每个任务都有许多位置,在此期间,它可能会由于异常而失败。ThreadPoolExecutor

我已经子类化并且我已经覆盖了应该提供在运行任务时遇到的任何未捕获异常的方法。但是,我似乎无法使其工作。ThreadPoolExecutorafterExecute

例如:

public class ThreadPoolErrors extends ThreadPoolExecutor {
    public ThreadPoolErrors() {
        super(  1, // core threads
                1, // max threads
                1, // timeout
                TimeUnit.MINUTES, // timeout units
                new LinkedBlockingQueue<Runnable>() // work queue
        );
    }

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if(t != null) {
            System.out.println("Got an error: " + t);
        } else {
            System.out.println("Everything's fine--situation normal!");
        }
    }

    public static void main( String [] args) {
        ThreadPoolErrors threadPool = new ThreadPoolErrors();
        threadPool.submit( 
                new Runnable() {
                    public void run() {
                        throw new RuntimeException("Ouch! Got an error.");
                    }
                }
        );
        threadPool.shutdown();
    }
}

此程序的输出是“一切都很好 - 情况正常!”,即使提交到线程池的唯一 Runnable 会引发异常。有什么线索可以说明这里发生了什么吗?

谢谢!


答案 1

警告:应该注意的是,此解决方案将在将来阻止调用线程。get()


如果要处理任务引发的异常,则通常最好使用 而不是 。CallableRunnable

Callable.call()允许抛出选中的异常,这些异常会传播回调用线程:

Callable task = ...
Future future = executor.submit(task);
// do something else in the meantime, and then...
try {
   future.get();
} catch (ExecutionException ex) {
   ex.getCause().printStackTrace();
}

如果抛出异常,则此情况将被包装在 中并由 抛出。Callable.call()ExecutionExceptionFuture.get()

这可能比子类化更可取。如果异常是可恢复的,它还为您提供了重新提交任务的机会。ThreadPoolExecutor


答案 2

文档中

注意:当操作显式或通过提交等方法包含在任务(如 FutureTask)中时,这些任务对象会捕获并维护计算异常,因此它们不会导致突然终止,并且内部异常不会传递给此方法。

当您提交 Runnable 时,它将被包装在未来。

您的 afterExecute 应该是这样的:

public final class ExtendedExecutor extends ThreadPoolExecutor {

    // ...

    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        if (t == null && r instanceof Future<?>) {
            try {
                Future<?> future = (Future<?>) r;
                if (future.isDone()) {
                    future.get();
                }
            } catch (CancellationException ce) {
                t = ce;
            } catch (ExecutionException ee) {
                t = ee.getCause();
            } catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
        if (t != null) {
            System.out.println(t);
        }
    }
}