程序中有一个定时任务,每10分钟把满足条件的任务从一个表迁移到另一张表,程序启动的时候数据库异常了一段时间,之后数据库恢复了。但是通过观察,发现此定时任务挂掉了,再也没有重启起来。
解决此问题的办法是要在任务线程的run方法中中捕获runtime异常,如果使用ScheduledExecutorFactoryBean,只要配置continueScheduledExecutionAfterException属性为true即可。Spring文档描述的很清楚:
setsup a JDK 1.5 ScheduledExecutorService
(bydefault: ScheduledThreadPoolExecutor
asimplementation) and exposes it for bean references.
Allowsfor registration of ScheduledExecutorTasks
,automatically starting the ScheduledExecutorService
oninitialization and cancelling it on destruction of the context. Inscenarios that just require static registration of tasks at startup,there is no need to access the ScheduledExecutorService
instanceitself in application code.
Notethat ScheduledExecutorService
usesa Runnable
instancethat is shared between repeated executions, in contrast to Quartzwhich instantiates a new Job for each execution.
WARNING: Runnables
submittedvia a native ScheduledExecutorService
areremoved from the execution schedule once they throw an exception. Ifyou would prefer to continue execution after such an exception,switch thisFactoryBean's"continueScheduledExecutionAfterException"
propertyto "true
对于定时任务,java原生支持有Timer,和ScheduledExecutor,前文中已经有介绍,参见:
http://blog.csdn.net/xiaojianpitt/article/details/7659422
如果使用spring的话,可以使用org.springframework.scheduling.concurrent.ScheduledExecutorFactoryBean,完全超越ScheduledExecutor。
ScheduledExecutorFactoryBean使用ScheduledThreadPoolExecutor
作为内部实现,定时调用使用
ScheduledThreadPoolExecutor
的方法
.
但是continueScheduledExecutionAfterException如何让异常任务继续运行的呢?代码是最好的说明:我们可以跟踪其源代码来找出:
protected Runnable getRunnableToSchedule(ScheduledExecutorTasktask) {
return this.continueScheduledExecutionAfterException
? new DelegatingErrorHandlingRunnable(task.getRunnable(),TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER)
: newDelegatingErrorHandlingRunnable(task.getRunnable(),TaskUtils.LOG_AND_PROPAGATE_ERROR_HANDLER);
}
我们可以看到,如果我们设置了在异常之后继续执行任务,那么在实现中对Runnable进行了封装,封装类为DelegatingErrorHandlingRunnable,我们继续追踪其源代码:
publicclass DelegatingErrorHandlingRunnable implements Runnable{
…
public void run(){
try{
this.delegate.run();
}catch(Throwable ex){
this.errorHandler.handleError(ex):
}
}
…
}
注意这个DelegatingErrorHandlingRunnable实现了Runnable接口,作为原始任务的代理,需要注意的是run里面的trycatch 对异常的处理,在这里catch了Throwable异常(具体可见java异常结构),这java异常的基类,包括Error和RuntimeException所有的java异常都被捕获,之后交给了errorHandler处理,ErrorHandler可以继续抛出异常或者不抛出,其中TaskUtils.LOG_AND_SUPPRESS_ERROR_HANDLER只是log打印异常,TaskUtils.LOG_AND_PROPAGATE_ERROR_HANDLER要继续抛出runtimeException。
TiskUtils
代码如下:
public
static
final
ErrorHandlerLOG_AND_SUPPRESS_ERROR_HANDLER =
new
LoggingErrorHandler();
…
.
static class LoggingErrorHandler implements ErrorHandler {
private final Log logger =LogFactory.getLog(LoggingErrorHandler.class);
public void handleError(Throwable t) {
if (logger.isErrorEnabled()) {
logger.error("Unexpected error occurred in scheduledtask.", t);
}
}
}
….
static class PropagatingErrorHandler extends LoggingErrorHandler {
public void handleError(Throwable t) {
super.handleError(t);
ReflectionUtils.rethrowRuntimeException(t);
}
}
ReflectionUtils代码如下:
public static void rethrowRuntimeException(Throwable ex) {
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
if (ex instanceof Error) {
throw (Error) ex;
}
handleUnexpectedException(ex);
}