tl;博士
任何转义方法的异常都会停止所有进一步的工作,恕不另行通知。run
始终在方法中使用 try-catch
。如果您希望计划的活动继续进行,请尝试恢复。run
@Override
public void run ()
{
try {
doChore();
} catch ( Exception e ) {
logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() );
}
}
问题
这个问题指的是调度执行器服务
的关键技巧:任何到达执行器的抛出异常或错误都会导致执行器停止。不再调用 Runnable,不再做任何工作。这种停工悄悄发生,你不会被告知。这篇淘气的语言博客文章有趣地叙述了了解这种行为的艰难方法。
解决方案
yegor256的答案和arun_suresh的答案似乎都基本正确。这些答案有两个问题:
错误和异常 ?
在Java中,我们通常只捕获异常,而不是错误。但在 ScheduledExecutorService 的这种特殊情况下,未能捕获任何一个都意味着停工。所以你可能想抓住两者。我不是100%确定这一点,不完全知道捕获所有错误的含义。如果需要,请纠正我。
捕获错误和异常的一个原因可能涉及在任务中使用库。请参阅jannis的评论。
捕获异常和错误的一种方法是捕获它们的超类,例如Storeable。
} catch ( Throwable t ) {
...而不是...
} catch ( Exception e ) {
最简单的方法:只需添加一个Try-Catch
但这两个答案都有点复杂。为了记录在案,我将展示最简单的解决方案:
始终将 Runnable 的代码包装在 Try-Catch 中,以捕获任何和所有异常和错误。
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() );
}
};
老式语法
老式的方式,在lambs之前。
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
的任何运行
方法中始终使用通用方法似乎是明智的。对于可调用
对象的任何调用
方法,同上。try-catch( Exception† e )
完整的示例代码
在实际工作中,我可能会单独定义而不是嵌套。但这确实是一个整洁的多合一示例。Runnable
package com.basilbourque.example;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* Demo `ScheduledExecutorService`
*/
public class App {
public static void main ( String[] args ) {
App app = new App();
app.doIt();
}
private void doIt () {
// Demonstrate a working scheduled executor service.
// Run, and watch the console for 20 seconds.
System.out.println( "BASIL - Start." );
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture < ? > handle =
scheduler.scheduleWithFixedDelay( new Runnable() {
public void run () {
try {
// doChore ; // Do business logic.
System.out.println( "Now: " + ZonedDateTime.now( ZoneId.systemDefault() ) ); // Report current moment.
} catch ( Exception e ) {
// … handle exception/error. Trap any unexpected exception here rather to stop it reaching and shutting-down the scheduled executor service.
// logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + e.getStackTrace() );
} // End of try-catch.
} // End of `run` method.
} , 0 , 2 , TimeUnit.SECONDS );
// Wait a long moment, for background thread to do some work.
try {
Thread.sleep( TimeUnit.SECONDS.toMillis( 20 ) );
} catch ( InterruptedException e ) {
e.printStackTrace();
}
// Time is up. Kill the executor service and its thread pool.
scheduler.shutdown();
System.out.println( "BASIL - Done." );
}
}
运行时。
巴西尔 - 开始。
现在:2018-04-10T16:46:01.423286-07:00[美国/Los_Angeles]
现在:2018-04-10T16:46:03.449178-07:00[美国/Los_Angeles]
现在:2018-04-10T16:46:05.450107-07:00[美国/Los_Angeles]
现在:2018-04-10T16:46:07.450586-07:00[美国/Los_Angeles]
现在:2018-04-10T16:46:09.456076-07:00[美国/Los_Angeles]
现在:2018-04-10T16:46:11.456872-07:00[美国/Los_Angeles]
现在:2018-04-10T16:46:13.461944-07:00[美国/Los_Angeles]
现在:2018-04-10T16:46:15.463837-07:00[美国/Los_Angeles]
现在:2018-04-10T16:46:17.469218-07:00[美国/Los_Angeles]
现在:2018-04-10T16:46:19.473935-07:00[美国/Los_Angeles]
巴西尔 - 完成。
另一个例子
这是另一个示例。在这里,我们的任务意味着运行大约二十次,每五秒钟一次,持续一分钟。但是在第五次运行中,我们抛出了一个异常。
public class App2
{
public static void main ( String[] args )
{
ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor();
final AtomicInteger counter = new AtomicInteger( 0 );
Runnable task = ( ) -> {
int c = counter.incrementAndGet();
if ( c > 4 )
{
System.out.println( "THROWING EXCEPTION at " + Instant.now() );
throw new IllegalStateException( "Bogus exception. c = " + c + ". " + Instant.now() ); // Notice how this exception is silently swallowed by the scheduled executor service, while causing a work stoppage.
}
System.out.println( "Task running. c = " + c + ". " + Instant.now() );
};
ses.scheduleAtFixedRate( task , 0 , 5 , TimeUnit.SECONDS );
try { Thread.sleep( Duration.ofMinutes( 1 ).toMillis() ); }catch ( InterruptedException e ) { e.printStackTrace(); }
System.out.println( "Main thread done sleeping. " + Instant.now() );
ses.shutdown();
try { ses.awaitTermination( 1 , TimeUnit.MINUTES ); }catch ( InterruptedException e ) { e.printStackTrace(); }
}
}
运行时。
Task running. c = 1. 2021-10-14T20:09:16.317995Z
Task running. c = 2. 2021-10-14T20:09:21.321536Z
Task running. c = 3. 2021-10-14T20:09:26.318642Z
Task running. c = 4. 2021-10-14T20:09:31.318320Z
THROWING EXCEPTION at 2021-10-14T20:09:36.321458Z
Main thread done sleeping. 2021-10-14T20:10:16.320430Z
通知:
- 异常被计划的执行程序服务以静默方式吞噬。
- 发生工作停止。没有安排进一步执行我们的任务。这又是一个无声的问题。
因此,当您的任务引发异常时,您会得到最糟糕的结果:无声停工,没有解释。
如上所述,解决方案:始终在方法中使用 a。try-catch
run
† 或者可能是可抛出
的而不是异常
来捕获错误
对象。