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