石英:防止jobs.xml中的作业的并发实例

这应该很容易。 我使用的是在Apache Tomcat 6.0.18下运行的Quartz,而且我有一个jobs.xml文件 ,它可以设置每分钟运行的计划作业。

我想要做的是,如果在下一次触发时间到来的时候,工作还在运行,我不想开始新的工作,所以我可以让旧的实例完成。

有没有办法指定这在jobs.xml(防止并发实例)?

如果不是的话,有没有办法在我的应用程序的Job实现中(这是通过JobExecutionContext ?)共享访问内存中的单例,所以我可以自己处理并发? (并检测前一个实例是否正在运行)


更新:在文档中陷入困境之后,我正在考虑一些方法,但要么不知道如何让它们工作,或者有问题。

  1. 使用StatefulJob 。 这可以防止并发访问…但我不知道如果我使用它会发生什么其他的副作用,我也想避免以下情况:

    假设触发时间是每一分钟,即触发#0 =在时间0,触发#1 = 60000msec,#2 = 120000,#3 = 180000等,并且在时间0触发#0触发我的工作需要130000msec。 用简单的Job,当作业触发器#0仍在运行时,它将执行触发器#1和#2。 使用StatefulJob,这将执行触发器#1和#2,在#0在130000结束后立即执行。我不想要那个,我想要#1和#2不运行,并且下一个运行作业的触发器应该发生在#3(180000msec)。 所以我仍然需要使用StatefulJob来做其他事情,以便按照我想要的方式工作,所以我没有看到使用它的好处。

  2. 使用TriggerListener从vetoJobExecution()返回true。

    虽然实现接口看起来很简单,但我必须弄清楚如何以声明方式设置TriggerListener的一个实例。 找不到xml文件的文档 。

  3. 使用实现Job的我的类拥有的static共享线程安全对象(例如信号量或其他)。

    我不喜欢在Tomcat / Quartz中通过static关键字使用单例的想法,不确定是否有副作用。 另外,我真的不希望他们成为真正的单身人士,只是与特定的工作定义有关。

  4. 实现我自己的触发器 ,它扩展了SimpleTrigger,并包含可以运行自己的TriggerListener的共享状态。

    再一次,我不知道如何设置XML文件来使用这个触发器,而不是标准的<trigger><simple>...</simple></trigger>

当你的Quartz工作醒来时,你可以这样做:

 JobDetail existingJobDetail = sched.getJobDetail(jobName, jobGroup); if (existingJobDetail != null) { List<JobExecutionContext> currentlyExecutingJobs = (List<JobExecutionContext>) sched.getCurrentlyExecutingJobs(); for (JobExecutionContext jec : currentlyExecutingJobs) { if(existingJobDetail.equals(jec.getJobDetail())) { //String message = jobName + " is already running."; //log.info(message); //throw new JobExecutionException(message,false); } } //sched.deleteJob(jobName, jobGroup); if you want to delete the scheduled but not-currently-running job } 

还有另一个更简单的解决scheme。 可以给作业一个DisallowConcurrentExecution的注释,它可以防止多个并发的实例在运行。 在这里看到文档。

链接不断突破,这里是相关的示例。

 @DisallowConcurrentExecution public class ColorJob implements Job { 

dimitrisli的答案不完整,所以这里是我的。

当Quartz Job醒来时,它返回JobExecutionContext给你。 我假设你想用相同的触发器跳过作业。

  List<JobExecutionContext> jobs = jobExecutionContext.getScheduler().getCurrentlyExecutingJobs(); for (JobExecutionContext job : jobs) { if (job.getTrigger().equals(jobExecutionContext.getTrigger()) && !job.getJobInstance().equals(this)) { logger.info("There's another instance running, so leaving" + this); return; } } 

我们得到当前的作业上下文,并检查是否有一个以前的作业实例具有相同的触发器。 如果是这种情况,我们只是跳回来。

我完成了类似的工作,使得我的工作类实现了StatefulJob ,它确保在当前正在运行的作业结束之前不会启动其他作业。

希望有所帮助;)

PD:我使用JBoss实现了它,但是我不认为这有什么区别。

你可以把这个工作设置成一个有状态的工作吗?对于你创build的每一个触发器,设置这个工作的失火指令,如果错过了,就不会触发? 不知道你正在使用什么types的工作,但你必须做一些调查你的触发器types可用的失火指示。

感谢:D

对scaramouche的解决scheme有轻微的变化。

 List<JobExecutionContext> jobs = jobExecutionContext.getScheduler().getCurrentlyExecutingJobs(); for (JobExecutionContext job : jobs) { if (job.getTrigger().equals(jobExecutionContext.getTrigger()) && !job.getFireInstanceId().equals(jobExecutionContext.getFireInstanceId()) { logger.info("There's another instance running, so leaving" + this); return; } } 

当所有JobExecutions有一个实例时,scaramouche解决scheme失败(使用自定义JobFactory类返回单例,而不是每次执行都调用newInstance())

如果你正在使用org.springframework.scheduling.quartz.QuartzJobBean

 protected void executeInternal(JobExecutionContext context) throws JobExecutionException { try { Scheduler scheduler = context.getScheduler(); List<JobExecutionContext> jobs = scheduler.getCurrentlyExecutingJobs(); for (JobExecutionContext job : jobs) { if (job.getTrigger().equals(context.getTrigger()) && job.getJobDetail() != context.getJobDetail()) { LOG.warn("Ignored!"); return; } } ... } catch (SchedulerException e) { LOG.error("What a luck! :'(", e); } ... }