如何在scheduleWithFixedDelay抛出异常时重新启动计划?

时间:2012-05-30 08:29:41

标签: java exception-handling concurrency threadpool

我使用ScheduledExecutorService来安排一些需要定期运行的任务。 我想知道这个代码是否能够在发生异常时恢复计划。

ScheduledExecutorService service = Executors.newScheduledThreadPool(1);
this.startMemoryUpdateSchedule(service);//See below method

//Recursive method to handle exception when run schedule task
private void startMemoryUpdateSchedule(ScheduledExecutorService service) {
    ScheduledFuture<?> future = service.scheduleWithFixedDelay(new MemoryUpdateThread(), 1, UPDATE_MEMORY_SCHEDULE, TimeUnit.MINUTES);
    try {
        future.get();
    } catch (ExecutionException e) {
        e.printStackTrace();
        logger.error("Exception thrown for thread",e);
        future.cancel(true);
        this.startMemoryUpdateSchedule(service);
    } catch(Exception e) {
        logger.error("Other exception ",e);
    }
}

4 个答案:

答案 0 :(得分:1)

你可能应该在while(true)循环中包含try块,因为如果第一次运行没有抛出异常,你将退出你的方法,如果第二次调用抛出一个,你将无法捕获它。 / p>

我还会在自己的线程中运行递归调用,以避免在出现问题时发生StackOverFlow错误的风险。

所以它看起来像这样:

private void startMemoryUpdateSchedule(final ScheduledExecutorService service) {
    final ScheduledFuture<?> future = service.scheduleWithFixedDelay(new MemoryUpdateThread(), 1, UPDATE_MEMORY_SCHEDULE, TimeUnit.MINUTES);
    Runnable watchdog = new Runnable() {

        @Override
        public void run() {
            while (true) {
                try {
                    future.get();
                } catch (ExecutionException e) {
                    //handle it
                    startMemoryUpdateSchedule(service);
                    return;
                } catch (InterruptedException e) {
                    //handle it
                    return;
                }
            }
        }
    };
    new Thread(watchdog).start();
}

答案 1 :(得分:1)

尝试使用专为此目的而设计的VerboseRunnable jcabi-log类:

import com.jcabi.log.VerboseRunnable;
Runnable runnable = new VerboseRunnable(
  Runnable() {
    public void run() { 
      // do business logic, may Exception occurs
    }
  },
  true // it means that all exceptions will be swallowed and logged
);

现在,当任何人调用runnable.run()时,不会抛出任何异常。相反,它们被吞下并记录(到SLF4J)。

答案 2 :(得分:0)

我已经按照讨论的方式添加了循环。

public void startMemoryUpdateSchedule(final ScheduledExecutorService service) {

    boolean retry = false;

    do {

        ScheduledFuture<?> future = null;
        try {
            retry = false;
            future = service.scheduleWithFixedDelay(new MemoryUpdateThread(), 1, UPDATE_MEMORY_SCHEDULE, TimeUnit.SECONDS);
            future.get();
        } catch (ExecutionException e) {
            // handle
            future.cancel(true);
            retry = true;
        } catch(Exception e) {
            // handle
        }           

    } while (retry);

}

答案 3 :(得分:0)

ScheduledExecutorService.scheduleWithFixedDelay(Runnable, long, long, TimeUnit)抛出RejectedExecutionException(RuntimeException的子节点)==&gt;我们可以抓住它和它再次重试提交。

现在future.get()应该返回一次执行的结果,我们需要在循环中调用它。

此外,一次执行失败不会影响下一次计划执行,这会将ScheduledExecutorService与执行同一thread =&gt;中的计划任务的TimerTask区分开来。在TimerTask的情况下,一次执行失败将中止调度(http://stackoverflow.com/questions/409932/java-timer-vs-executorservice) 我们只需要捕获Future.get()抛出的所有三个异常,但是我们不能重新抛出它们,那么我们将无法获得后续执行的结果。

代码可以是:

public void startMemoryUpdateSchedule(final ScheduledExecutorService service) {
final ScheduledFuture<?> future;
    try {
        future = service.scheduleWithFixedDelay(new MemoryUpdateThread(),
                1, UPDATE_MEMORY_SCHEDULE, TimeUnit.SECONDS);
    } catch (RejectedExecutionException ree) {
        startMemoryUpdateSchedule(service);
        return;
    }
    while (true) {
        try {
            future.get();
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        } catch (ExecutionException ee) {
            Throwable cause = ee.getCause();
            // take action, log etc.
        } catch (CancellationException e) {
          // safety measure if task was cancelled by some external agent.
        }
    }
}