不知道大家在用Quartz的时候 有没有遇到这样一种情况:
触发器设定每3秒钟触发一次 ,但是工作需要10秒钟的执行时间.因此,在一次任务结束执行前,触发器已经错失触发
当这种情况下我们怎么处理呢,让我们一起学习一下......
还是先贴代码:
job类:StatefulDumbJob.java
- import java.text.SimpleDateFormat;
- import java.util.Calendar;
- import org.quartz.DisallowConcurrentExecution;
- import org.quartz.Job;
- import org.quartz.JobDataMap;
- import org.quartz.JobExecutionContext;
- import org.quartz.JobExecutionException;
- import org.quartz.PersistJobDataAfterExecution;
- @PersistJobDataAfterExecution
- @DisallowConcurrentExecution
- public class StatefulDumbJob implements Job {
- // 静态常量,作为任务在调用间,保持数据的键(key)
- // NUM_EXECUTIONS,保存的计数每次递增1
- // EXECUTION_DELAY,任务在执行时,中间睡眠的时间。本例中睡眠时间过长导致了错失触发
- public static final String NUM_EXECUTIONS = "NumExecutions";
- public static final String EXECUTION_DELAY = "ExecutionDelay";
- @Override
- public void execute(JobExecutionContext context)
- throws JobExecutionException {
- // 任务执行的时间
- SimpleDateFormat dateFormat = new SimpleDateFormat(
- "yyyy-MM-dd HH:mm:ss");
- String jobRunTime = dateFormat.format(Calendar.getInstance().getTime());
- System.err.println("---" + context.getJobDetail().getKey().getName() + " 在 : ["
- + jobRunTime + "] 执行了!!");
- // 任务执行计数 累加
- JobDataMap map = context.getJobDetail().getJobDataMap();
- int executeCount = 0;
- if (map.containsKey(NUM_EXECUTIONS)) {
- executeCount = map.getInt(NUM_EXECUTIONS);
- }
- executeCount++;
- map.put(NUM_EXECUTIONS, executeCount);
- // 睡眠时间: 由调度类重新设置值 ,本例为 睡眠10s
- long delay = 5000l;
- if (map.containsKey(EXECUTION_DELAY)) {
- delay = map.getLong(EXECUTION_DELAY);
- }
- try {
- Thread.sleep(delay);
- } catch (Exception ignore) {
- }
- // 睡眠醒来后,打印任务执行结束的信息
- System.err.println(" -" + context.getJobDetail().getKey().getName()
- + " 完成次数 : " + executeCount );
- }
- }
调度类: MisfireExample.java
- import static org.quartz.DateBuilder.nextGivenSecondDate;
- import static org.quartz.JobBuilder.newJob;
- import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
- import static org.quartz.TriggerBuilder.newTrigger;
- import java.text.SimpleDateFormat;
- import java.util.Date;
- import org.quartz.JobDetail;
- import org.quartz.Scheduler;
- import org.quartz.SchedulerFactory;
- import org.quartz.SchedulerMetaData;
- import org.quartz.SimpleTrigger;
- import org.quartz.impl.StdSchedulerFactory;
- public class MisfireExample {
- public static void main(String[] args) throws Exception {
- MisfireExample example = new MisfireExample();
- example.run();
- }
- public void run() throws Exception {
- // 任务执行的时间 格式化
- SimpleDateFormat dateFormat = new SimpleDateFormat(
- "yyyy-MM-dd HH:mm:ss");
- SchedulerFactory sf = new StdSchedulerFactory();
- Scheduler sched = sf.getScheduler();
- System.out.println("--------------- 初始化 -------------------");
- // 下一个第15秒
- Date startTime = nextGivenSecondDate(null, 15);
- // statefulJob1 每3s运行一次,但它会延迟10s
- JobDetail job = newJob(StatefulDumbJob.class)
- .withIdentity("statefulJob1", "group1")
- .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L) // 设置参数:睡眠时间 10s
- .build();
- SimpleTrigger trigger = newTrigger()
- .withIdentity("trigger1", "group1")
- .startAt(startTime)
- .withSchedule(
- simpleSchedule().withIntervalInSeconds(3)
- .repeatForever()).build();
- Date ft = sched.scheduleJob(job, trigger);
- System.out.println(job.getKey().getName() + " 将在: " + dateFormat.format(ft)
- + " 时运行.并且重复: " + trigger.getRepeatCount() + " 次, 每次间隔 "
- + trigger.getRepeatInterval() / 1000 + " 秒");
- // statefulJob2 将每3s运行一次 , 但它将延迟10s , 然后不断的迭代
- job = newJob(StatefulDumbJob.class)
- .withIdentity("statefulJob2", "group1")
- .usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L)// 设置参数:睡眠时间 10s
- .build();
- trigger = newTrigger()
- .withIdentity("trigger2", "group1")
- .startAt(startTime)
- .withSchedule(
- simpleSchedule()
- .withIntervalInSeconds(3)
- .repeatForever()
- // 设置错失触发后的调度策略
- .withMisfireHandlingInstructionNowWithRemainingCount()
- )
- .build();
- ft = sched.scheduleJob(job, trigger);
- System.out.println(job.getKey().getName() + " 将在: " + dateFormat.format(ft)
- + " 时运行.并且重复: " + trigger.getRepeatCount() + " 次, 每次间隔 "
- + trigger.getRepeatInterval() / 1000 + " 秒");
- System.out.println("------- 开始调度 (调用.start()方法) ----------------");
- sched.start();
- // 给任务留时间运行
- Thread.sleep(600L * 1000L);
- sched.shutdown(true);
- System.out.println("------- 调度已关闭 ---------------------");
- // 显示一下 已经执行的任务信息
- SchedulerMetaData metaData = sched.getMetaData();
- System.out.println("~~~~~~~~~~ 执行了 "
- + metaData.getNumberOfJobsExecuted() + " 个 jobs.");
- }
- }
-------------------------------------我是分割线----------------------------------------------------------
先说明 一个词 : misfire -- 指的是 错过了触发时间
你会注意到2个触发器具有相同的时间安排,相同的任务
触发器设定每3秒钟触发一次,但是工作需要10秒钟的执行时间
因此,在一次任务结束执行前,触发器已经错失触发(除非’错失触发时限’被设置为超过7秒)。
其中第二个任务设置自己的错失触发指示:.withMisfireHandlingInstructionNowWithRemainingCount()
所以当检测到丢失触发时,不会立即触发,而是忽略本次安排到下一个预定时间去触发
我们的结论 :
a) 本范例中,触发的间隔被设置为3秒,但是由于任务体执行间睡眠10秒,导致了错失触发的产生。
b) 实际运行的效果,2个任务执行的间隔为10秒。
c) 由于丢失触发时,job2的策略是立即触发,而job1是等待下一次机会触发。所以job2会赶在job1的前头,最终运行次数大于job1。
Quartz 的 Misfire处理规则:
调度(scheduleJob)或恢复调度(resumeTrigger,resumeJob)后不同的misfire对应的处理规则
CronTrigger
withMisfireHandlingInstructionDoNothing
——不触发立即执行
——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行
withMisfireHandlingInstructionIgnoreMisfires
——以错过的第一个频率时间立刻开始执行
——重做错过的所有频率周期后
——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行
withMisfireHandlingInstructionFireAndProceed
——以当前时间为触发频率立刻触发一次执行
——然后按照Cron频率依次执行
SimpleTrigger
withMisfireHandlingInstructionFireNow
——以当前时间为触发频率立即触发执行
——执行至FinalTIme的剩余周期次数
——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
withMisfireHandlingInstructionIgnoreMisfires
——以错过的第一个频率时间立刻开始执行
——重做错过的所有频率周期
——当下一次触发频率发生时间大于当前时间以后,按照Interval的依次执行剩下的频率
——共执行RepeatCount+1次
withMisfireHandlingInstructionNextWithExistingCount
——不触发立即执行
——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数
——以startTime为基准计算周期频率,并得到FinalTime
——即使中间出现pause,resume以后保持FinalTime时间不变
withMisfireHandlingInstructionNowWithExistingCount
——以当前时间为触发频率立即触发执行
——执行至FinalTIme的剩余周期次数
——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
withMisfireHandlingInstructionNextWithRemainingCount
——不触发立即执行
——等待下次触发频率周期时刻,执行至FinalTime的剩余周期次数
——以startTime为基准计算周期频率,并得到FinalTime
——即使中间出现pause,resume以后保持FinalTime时间不变
withMisfireHandlingInstructionNowWithRemainingCount
——以当前时间为触发频率立即触发执行
——执行至FinalTIme的剩余周期次数
——以调度或恢复调度的时刻为基准的周期频率,FinalTime根据剩余次数和当前时间计算得到
——调整后的FinalTime会略大于根据starttime计算的到的FinalTime值
MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT
——此指令导致trigger忘记原始设置的starttime和repeat-count
——触发器的repeat-count将被设置为剩余的次数
——这样会导致后面无法获得原始设定的starttime和repeat-count值
另外,如果任务数超过了Quartz的线程池中的线程数时,也会发生类似的情况 兄弟们可以参考一下下面的这篇博文:
http://www.cnblogs.com/makemelaugh/archive/2012/06/17/2533105.html