我使用ScheduledExecutorService定期执行一个方法。
p-code:
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<?> handle =
scheduler.scheduleWithFixedDelay(new Runnable() {
public void run() {
//Do business logic, may Exception occurs
}
}, 1, 10, TimeUnit.SECONDS);我的问题是:
如果run()抛出异常,如何继续调度?我是否应该尝试捕获方法run()中的所有异常?或者任何内置的回调方法来处理异常?谢谢!
发布于 2011-08-01 14:02:18
您应该像这样使用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();
}发布于 2014-07-23 12:44:45
tl;dr
任何对run方法进行转义的异常都会在没有通知的情况下停止所有后续的工作。
始终在run方法中使用 try-catch。如果您希望计划的活动继续,请尝试恢复。
@Override
public void run ()
{
try {
doChore();
} catch ( Exception e ) {
logger.error( "Caught exception in ScheduledExecutorService. StackTrace:\n" + t.getStackTrace() );
}
}问题所在
这个问题涉及到ScheduledExecutorService的关键技巧:任何抛出的异常或错误到达executor都会导致executor停止。不再调用Runnable,不再完成任何工作。此工作停止是静默发生的,您将不会收到通知。这个顽皮的语言blog posting有趣地讲述了学习这种行为的艰难过程。
解决方案
answer by yegor256和answer by arun_suresh似乎都是基本正确的。这些答案有两个问题:
错误和异常?
在Java语言中,我们通常只捕获exceptions,而不捕获errors。但在ScheduledExecutorService的这种特殊情况下,没有捕捉到任何一个都将意味着工作停止。所以你可能想同时抓住这两个。我不是百分之百确定这一点,因为我不完全知道捕获所有错误的含义。如果需要,请纠正我。
捕获错误和异常的一个原因可能涉及在任务中使用库。请参阅comment by jannis。
捕获异常和错误的一种方法是捕获它们的超类,例如Throwable。
} catch ( Throwable t ) {…而不是…
} catch ( Exception e ) {最简单的方法:只需添加Try-Catch
但是这两个答案都有点复杂。为了记录在案,我将展示最简单的解决方案:
总是将Runnable的代码包装在Try-Catch中,以捕获任何和所有的异常和错误。
Lambda语法
使用lambda (在Java8和更高版本中)。
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() );
}
};老式语法
传统的方式,在lambdas之前。
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是什么,对我来说,在run的任何Runnable方法中总是使用通用try-catch( Exception† e )似乎是明智的。Callable的任何call方法也是如此。
完整的示例代码
在实际工作中,我可能会单独定义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." );
}
}运行时。
BASIL Start。
现在: 2018-04-10T16:46:01.423286-07:00America/Los_Angeles
现在: 2018-04-10T16:46:03.449178-07:00America/Los_Angeles
现在: 2018-04-10T16:46:05.450107-07:00America/Los_Angeles
现在: 2018-04-10T16:46:07.450586-07:00America/Los_Angeles
现在: 2018-04-10T16:46:09.456076-07:00America/Los_Angeles
现在: 2018-04-10T16:46:11.456872-07:00America/Los_Angeles
现在: 2018-04-10T16:46:13.461944-07:00America/Los_Angeles
现在: 2018-04-10T16:46:15.463837-07:00America/Los_Angeles
现在: 2018-04-10T16:46:17.469218-07:00America/Los_Angeles
现在: 2018-04-10T16:46:19.473935-07:00America/Los_Angeles
罗勒做的。
另一个例子
这是另一个例子。在这里,我们的任务应该运行大约20次,每5秒运行一次,每分钟运行一次。但是在第五次运行时,我们抛出了一个异常。
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注意:
因此,当你的任务抛出异常时,你会得到最坏的结果:没有任何解释的静默停止工作。
如上所述,解决方案是:始终在run方法中使用try-catch。
用Throwable代替Exception来捕获Error对象,也可以使用†。
发布于 2012-05-10 23:47:30
另一种解决方案是在Runnable中接受一个异常。您可以使用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
);https://stackoverflow.com/questions/6894595
复制相似问题