如何在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. } } }