ScheduledExecutorServiceexception处理

我使用ScheduledExecutorService定期执行一个方法。

p代码:

ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture<?> handle = scheduler.scheduleWithFixedDelay(new Runnable() { public void run() { //Do business logic, may Exception occurs } }, 1, 10, TimeUnit.SECONDS); 

我的问题:

如何继续调度程序,如果run()抛出exception? 我应该尝试在方法run()捕获所有exception? 或者任何内置的callback方法来处理exception? 谢谢!

您应该使用scheduler.scheduleWithFixedDelay(...)返回的ScheduledFuture对象,如下所示:

 ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture<?> handle = scheduler.scheduleWithFixedDelay(new Runnable() { public void run() { throw new RuntimeException("foo"); } }, 1, 10, TimeUnit.SECONDS); // Create and Start an exception handler thread // pass the "handle" object to the thread // Inside the handler thread do : .... try { handle.get(); } catch (ExecutionException e) { Exception rootException = e.getCause(); } 

问题

这个问题涉及到ScheduledExecutorService的关键技巧: 任何抛出exception或到达执行程序的错误都会导致执行程序停止。 Runnable没有更多的调用,没有更多的工作完成。 这个工作停顿是悄然发生的,你不会被通知。 这个顽皮的博客张贴有趣地叙述了难以了解这种行为的方式。

解决scheme

yegor256的回答和arun_suresh的回答似乎基本上是正确的。 两个问题与这些答案:

  • 捕获错误以及例外
  • 有点复杂

错误例外?

在Java中,我们通常只捕获exception ,而不是错误 。 但是在这个ScheduledExecutorService的特殊情况下,如果没有捕获任何一个,将意味着停工。 所以你可能要抓住两个。 我不是100%肯定这一点,不完全知道捕捉所有错误的影响。 如果需要,请纠正我。

捕获exception和错误的一种方法是捕获它们的超类Throwable 。

 } catch ( Throwable t ) { 

…而不是…

 } catch ( Exception e ) { 

最简单的方法:只需添加一个Try-Catch

但是这两个答案都有点复杂。 为了logging,我将展示最简单的解决scheme:

总是将你的Runnable的代码封装在Try-Catch中,以捕获任何exception错误。

Lambda语法

使用lambda(在Java 8和更高版本中)。

 final Runnable someChoreRunnable = () -> { try { doChore(); } catch ( Throwable t ) { // Catch Throwable rather than Exception (a subclass). logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() ); } }; 

古板的语法

老式的方式,在lambda之前。

 final Runnable someChoreRunnable = new Runnable() { @Override public void run () { try { doChore(); } catch ( Throwable t ) { // Catch Throwable rather than Exception (a subclass). logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() ); } } }; 

在每个可运行/可调用中

无论ScheduledExecutorService如何,在Runnable 任何 run方法中总是使用一般try-catch( Exception† e )似乎是明智的。 对于Callable任何call方法,同上。


†或者也许Throwable而不是Exception来捕获Error对象。

另一个解决scheme是吞下Runnable的exception。 您可以使用jcabi-log中的方便的VerboseRunnable类,例如:

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

我知道这是一个老问题,但是如果有人使用ScheduledExecutorService延迟CompletableFuture ,那么应该这样处理:

 private static CompletableFuture<String> delayed(Duration delay) { CompletableFuture<String> delayed = new CompletableFuture<>(); executor.schedule(() -> { String value = null; try { value = mayThrowExceptionOrValue(); } catch (Throwable ex) { delayed.completeExceptionally(ex); } if (!delayed.isCompletedExceptionally()) { delayed.complete(value); } }, delay.toMillis(), TimeUnit.MILLISECONDS); return delayed; } 

并在CompletableFuture处理exception:

 CompletableFuture<String> delayed = delayed(Duration.ofSeconds(5)); delayed.exceptionally(ex -> { //handle exception return null; }).thenAccept(value -> { //handle value });