如何使用ScheduledExecutorService处理重复任务中的错误?

时间:2012-12-21 16:40:19

标签: java multithreading concurrency error-handling scheduledexecutorservice

我有一个ScheduledExecutorService,会定期通过Runnable调用scheduleWithFixedDelay()(可能会使用scheduleAtFixedRate()代替)。

现在正在考虑如果发生错误该怎么办。如果它是不能从(*)中轻易恢复的东西,我想选择停止所有进一步的调用,但不确定最好的方法。

显然不能从Runnable中检查异常,所以我们将非常感谢您如何选择以下内容:

scheduledFuture.cancel(false);
...or...
scheduledFuture.cancel(true);
...or...
scheduledExecutorService.shutdown();
...or...
scheduledExecutorService.shutdownNow();
...or...
Throw a custom RuntimeException myself?
...or...
Something else?

(*)想知道一般情况但是如果有人感兴趣,我正在查看的已检查异常是从DocumentBuilderFactory.newDocumentBuilder()抛出的ParserConfigurationException。如果这被抛出,则表示存在严重问题,因此我基本上喜欢完全停止计划而不是每次都可能重复错误。

2 个答案:

答案 0 :(得分:1)

您可以使用CallableFuture。这将允许您从异步任务中抛出已检查的异常,但仍然可以根据需要捕获和处理每个任务。

如果您使用该方法,那么允许任务本身决定如何处理异常可能是最有意义的。看到这个答案:

但是,如果你想在任务本身之外处理异常,那么我认为每个任务都需要另一个线程。这是一个可能的选择:

    ScheduledExecutorService scheduleExecutor;
    scheduleExecutor = = Executors.newScheduledThreadPool(10); // or whatever

    ExecutorService workerExecutor;
    workerExecutor = Executors.newSingleThreadExecutor(); // or whatever

    public void schedule(final long fixedDelay) {

      scheduleExecutor.scheduleWithFixedDelay(new Runnable() {

        @Override
        public void run() {

            Future<Void> future = workerExecutor.submit(new Callable<Void>() {

                @Override
                public Void call() throws Exception {

                    // Do work here. Throw appropiate exception as needed.

                    return null;
                }
            });

            // Now you can catch and handle the exception in whatever
            // way you need to. You can cancel just this task (which is likely
            // redundant by this point), or you can choose to shutdown
            // all other scheduled tasks (which I doubt is what you want).

            try {
                future.get();
            } catch (Exception e) {
                future.cancel(true);
            }

        }
    }, 0, fixedDelay, TimeUnit.MILLISECONDS);

}

答案 1 :(得分:1)

基于上面的一些有用的评论,这是我当前代码的要点 - 还有一些问题仍然存在,欢迎任何进一步的评论:

public class ScheduledTask implements Runnable {
    // Configurable values
    private static final int CORE_THREAD_POOL_SIZE = 1;
    private static final int INITIAL_DELAY_MS = 0;
    private static final int INTERVAL_MS = 1000;

    private final ScheduledExecutorService scheduledExecutorService = 
        Executors.newScheduledThreadPool(ScheduledTask.CORE_THREAD_POOL_SIZE);

    private ScheduledFuture<?> scheduledFuture;

    public void run() {
        try {
            try {
                // Do stuff
            } catch RecoverableCheckedException rce { // E.g. SAXException
                // Log and handle appropriately
            }
        } catch UnrecoverableCheckedException uce { // E.g. ParserConfigurationException
            // Not 100% happy with this. It means the caller would need to call
            // getCause() to get the real Exception in this case. But other 
            // RuntimeExceptions wouldn't be wrapped. Could consider catching
            // and wrapping all RuntimeExceptions but I like that even less!
            throw new RuntimeException(uce);
        }
    }

    public boolean isScheduling() {
        return (this.scheduledFuture != null)
               && (!this.scheduledFuture.isDone());
    }

    // May not be needed but provided in case this class is shared.
    public boolean isShutdown() {
        return scheduledExecutorService.isShutdown();
    }

    public void start() {
        // If the Executor Service has already been shutdown, would expect
        // a RejectedExecutionException to be thrown here(?) Not sure what
        // would happen if this method were called when isScheduling() is
        // true?
        this.scheduledFuture = 
            this.scheduledExecutorService.scheduleWithFixedDelay(
                this,
                ScheduledTask.INITIAL_DELAY_MS,
                ScheduledTask.INTERVAL_MS,
                TimeUnit.MILLISECONDS);
    }

    // To be called once at the very end - e.g. on program termination.
    public void shutdown() {
        this.scheduledExecutorService.shutdown();
    }
}