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

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

 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); } } 

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

我还会在自己的线程中运行递归调用,以避免在事情变坏时出现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(); } 

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

 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()不会抛出任何exception。 相反,他们被吞下并记录(到SLF4J)。

我已经讨论过添加了循环。

 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); } 

ScheduledExecutorService.scheduleWithFixedDelay(Runnable, long, long, TimeUnit)抛出RejectedExecutionException (RuntimeException的子节点)==>我们可以捕获它并再次重试提交。

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

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

代码可以是:

 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. } } }