49

我有一个 Java 程序,它每 20 秒从 Spring Qquartz 执行一次。有时只需几秒钟即可执行,但随着数据变大,我确信它会运行 20 秒或更长时间。

如何防止 Quartz 在一个实例仍在执行时触发/触发作业?解雇 2 个在数据库上执行相同操作的作业不会那么好。有没有办法可以进行某种同步?

4

7 回答 7

147

石英 1

如果你改变你的类来实现 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*/}
}

石英 2

在 Quartz 2.0 版中,StatefulJob已弃用。现在建议改用注解,例如

@DisallowConcurrentExecution
public class YourJob implements org.quartz.Job {
    void execute(JobExecutionContext context) {/*implementation omitted*/}
}
于 2009-10-28T16:52:14.700 回答
31

如果您需要做的只是每 20 秒开火一次,那么 Quartz 是严重的矫枉过正。java.util.concurrent.ScheduledExecutorService应该完全足以胜任这项工作。

ScheduledExecutorService还为调度提供了两种语义。“固定速率”将尝试每 20 秒运行一次您的作业,而不考虑重叠,而“固定延迟”将尝试在第一个作业结束和下一个作业开始之间留出 20 秒。如果你想避免重叠,那么固定延迟是最安全的。

于 2009-10-28T11:09:50.540 回答
20

以防万一有人引用这个问题,StatefulJob 已被弃用。他们现在建议您改用注释...

@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class TestJob implements Job {

这将解释这些注释的含义......

注释引起的行为正如它们的名称所描述的那样 - 不允许作业的多个实例同时运行(考虑这样一种情况,即作业在其 execute() 方法中有代码需要 34 秒才能运行,但它被调度为每 30 秒重复一次的触发器),并且在每次执行后将其 JobDataMap 内容重新保存在调度程序的 JobStore 中。就本示例而言,只有 @PersistJobDataAfterExecution 注释是真正相关的,但最好将 @DisallowConcurrentExecution 注释与它一起使用,以防止保存的数据出现竞争条件。

于 2011-05-16T15:58:25.730 回答
4

如果您使用弹簧石英,我认为您必须像这样配置

    <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>
于 2014-08-27T14:05:34.780 回答
2

我不确定你想要同步,因为第二个任务会阻塞直到第一个任务完成,你最终会积压。您可以将作业放入队列中,但根据您的描述,队列可能会无限增长。

我会调查ReadWriteLock,并让您的任务在运行时设置一个锁。未来的任务可以检查此锁,如果旧任务仍在运行,则立即退出。我从经验中发现这是解决这个问题的最可靠方法。

也许还会产生警告,以便您知道遇到问题并相应地增加时间间隔?

于 2009-10-28T11:10:05.237 回答
0

把它们排成一个队列

即使时间超过 20 秒,当前作业也应该完成,然后应该从队列中获取下一个作业。

或者您也可以将时间增加到某个合理的数量。

于 2009-10-28T11:07:53.727 回答
0

您可以使用信号量。当信号量被取走时,放弃第二个工作,等到下一个触发时间。

于 2009-10-28T11:58:30.630 回答