我有一个Java程序,每20秒从Spring Qquartz执行一次。有时它只需要几秒钟就能执行完,但随着数据变得越来越大,我确信它会运行20秒甚至更长时间。
如何防止Quartz在一个实例仍在执行时触发作业/任务?同时执行相同的数据库操作可能不是很好。我能否进行某种形式的同步?
我有一个Java程序,每20秒从Spring Qquartz执行一次。有时它只需要几秒钟就能执行完,但随着数据变得越来越大,我确信它会运行20秒甚至更长时间。
如何防止Quartz在一个实例仍在执行时触发作业/任务?同时执行相同的数据库操作可能不是很好。我能否进行某种形式的同步?
如果你将你的类改为实现StatefulJob而不是Job,Quartz会自动处理这个问题。从StatefulJob javadoc中可以看到:
有状态作业不允许同时执行,这意味着在execute(xx)方法完成之前发生的新触发器将会被延迟。
StatefulJob扩展了Job,并没有添加任何新方法,因此,为了获得所需的行为,你只需要更改如下内容:
public class YourJob implements org.quartz.Job {
void execute(JobExecutionContext context) {/*implementation omitted*/}
}
变成这样:
public class YourJob implements org.quartz.StatefulJob {
void execute(JobExecutionContext context) {/*implementation omitted*/}
}
在Quartz 2.0版本中,StatefulJob
已被弃用。现在建议使用注释来代替,例如:
@DisallowConcurrentExecution
public class YourJob implements org.quartz.Job {
void execute(JobExecutionContext context) {/*implementation omitted*/}
}
java.util.concurrent.ScheduledExecutorService
应该是完全足够的。
ScheduledExecutorService
还提供了两种调度语义。"fixed rate"将尝试每20秒运行一次作业,而不考虑重叠,而"fixed delay"将尝试在第一个作业结束和下一个作业开始之间留出20秒的时间。如果你想避免重叠,那么使用fixed-delay是最安全的。TaskScheduler
。 - Michael Piefel提醒一下,如果有人参考这个问题,StatefulJob已被弃用。现在他们建议使用注释代替...
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class TestJob implements Job {
这将解释那些注释的含义...
这些注释会导致行为就像它们的名字描述的那样 - 不允许多个实例并发运行(考虑一种情况,其中一个作业在其execute()方法中有需要34秒才能完成的代码,但它被安排在每30秒重复一次的触发器中),并且在每次执行后,它的JobDataMap内容将重新持久化到调度程序的JobStore中。对于本示例,只有@PersistJobDataAfterExecution注释是真正相关的,但最好始终将@DisallowConcurrentExecution注释与其一起使用,以防止保存数据时出现竞争条件。
如果您使用Spring Quartz,我认为您需要进行以下配置:
<bean id="batchConsumerJob"class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="myScheduler" />
<property name="targetMethod" value="execute" />
<property name="concurrent" value="false" />
</bean>
MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
jobDetail.setConcurrent(false);
jobDetail.setBeanName("Job_" + jobId);
jobDetail.setTargetObject(job);
jobDetail.setTargetMethod("execute");
jobDetail.setConcurrent(false);
- Amare我建议研究读写锁,并让你的任务在运行时设置锁。未来的任务可以检查此锁,如果旧任务仍在运行,则立即退出。从我的经验来看,这是最可靠的方法。
或许还可以发出警告,以便你知道遇到问题并相应地增加时间间隔?
”你可以使用信号量。当信号量被占用时,放弃第二个任务并等待下一次触发时间。
@PersistJobDataAfterExecution
。 - Dónal