Java定时任务Quartz之触发器Trigger

文章目录

一、触发器Trigger种类

从类的继承图上可以看出一共有四种:

Java定时任务Quartz之触发器Trigger

触发器名称 作用
SimpleTrigger 指定从某一个时间开始,以一定的时间间隔(默认单位时毫秒)执行任务。但是没办法指定一个月执行一次(每个月时间间隔不一致)
CalendarIntervalTrigger 指定从摸一个时间开始,以一定的时间间隔执行任务,支持指定间隔单位有妙、分钟、小时、天、月、年和星期
DailyTimetervalTrigger 指定每天某一个时间间隔,以一定的时间间隔执行任务,并且支持指定星期
CronTrigger 适合于更复杂的任务,它支持类型于Linux Cron的语法,基本上包含了上面三个Trigger的绝大部分功能

二、触发器Trigger实例

2.1. Trigger常用属性

我们在TriggerBuilder中可以看到有如下的属性

public class TriggerBuilder<T extends Trigger> {

    private TriggerKey key;
    private String description;
    private Date startTime = new Date();
    private Date endTime;
    private int priority = Trigger.DEFAULT_PRIORITY;
    private String calendarName;
    private JobKey jobKey;
    private JobDataMap jobDataMap = new JobDataMap();
    
    private ScheduleBuilder<?> scheduleBuilder = null;
    
    private TriggerBuilder() {
        
    }
}
2.1.1. TriggerKey

通过最后源码发现,name 不能为null,否则抛异常,groupnull 时,使用默认值 DEFAULT

public Key(String name, String group) {
   if(name == null)
     throw new IllegalArgumentException("Name cannot be null.");
   this.name = name;
   if(group != null)
     this.group = group;
   else
     this.group = DEFAULT_GROUP;
}
2.1.2. 开始和结束时间

我们可以看到默认就是当前时间,意味立即执行。当然TriggerBuilder也提供了一个方法设置立即执行:

public TriggerBuilder<T> startNow() {
  this.startTime = new Date();
  return this;
}

也支持单独设置开始时间和结束时间:

public TriggerBuilder<T> startAt(Date triggerStartTime) {
  this.startTime = triggerStartTime;
  return this;
}

public TriggerBuilder<T> endAt(Date triggerEndTime) {
  this.endTime = triggerEndTime;
  return this;
}
2.1.3. 优先级

如果 Trigger 很多,或者 Quartz 线程池的工作线程太少,Quartz 可能没有足够的资源同时触发所有的 Trigger,这种情况下可能希望控制哪些 Trigger 优先使用 Quartz 的工作线程。

默认优先级是 :public static final int DEFAULT_PRIORITY = 5;优先级越小优先级越高。对于同时触发的Trigger 才会比较优先级。

2.1.4. 过期策略

这个我们在TriggerBuilder中没有看到,但是在抽象触发器AbstractTrigger是有的,默认是:

private int misfireInstruction = MISFIRE_INSTRUCTION_SMART_POLICY;  // 忽略

这个策略是在Trigger 接口中定义的,而设置这个策略需要在不同的TriggerScheduleBuilder中设置

CronScheduleBuilder支持三种:

  • withMisfireHandlingInstructionIgnoreMisfires(): 忽略所有的超时状态,按照触发器的策略执行
  • withMisfireHandlingInstructionFireAndProceed(): 立刻执行一次,然后就按照正常的计划执行
  • withMisfireHandlingInstructionDoNothing(): 目前不执行,然后就按照正常的计划执行

SimpleScheduleBuilder支持五种:

  • withMisfireHandlingInstructionIgnoreMisfires():忽略策略
  • withMisfireHandlingInstructionFireNow():立即执行策略
  • withMisfireHandlingInstructionNextWithExistingCount()重新安排下一步,现有计数
  • withMisfireHandlingInstructionNextWithRemainingCount()重新安排下一个,剩余计数
  • withMisfireHandlingInstructionNowWithExistingCount()(默认)对现有的重新开始计数执行
  • withMisfireHandlingInstructionNowWithRemainingCount()重新安排,重复计数

2.2. 产生错失/补偿的原因

  • 系统因为某些原因被重启
  • Trigger 被暂停(suspend)的一段时间里,有些任务可能会被 misfire
  • 线程池中所有线程都被占用,导致任务无法被触发执行,造成 misfire
  • 有状态任务在下次触发时间到达时,上次执行还没有结束

三、Cron表达式

在线验证网站:https://cron.qqe2.com/

3.1. 简述

Cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或者7个域,每一个域代表一个含义,Cron有如下两种格式:

  • 秒(Seconds) 分钟(Minutes) 小时(Hours) 日期(DayofMonth) 月份(Month) 星期(DayofWeek) 年(Year)
  • 秒(Seconds) 分钟(Minutes) 小时(Hours) 日期(DayofMonth) 月份(Month) 星期(DayofWeek)

3.2. 结构

字段 范围值 允许的特殊字符 序号
秒(Seconds) 0-59的整数 , - * / 1
分(Minutes) 0-59的整数 , - * / 2
小时(Hours) 0-23的整数 , - * / 3
日期(DayofMonth) 1-31的整数(实际月天数为准) , - * ? / L W C 4
月份(Month) 1-12的整数 , - * / 5
周(DayofWeek) 1-7的整数 , - * ? / L C # 6
年(可选,留空)(Year) 1970-2099 , - * / 7

3.3. 特殊字符含义

  • *(星花):表示匹配该域的任何值。例如在Minutes域使用*,即表示每分钟都会触发事件。
  • ?(问号): 只在日期域和星期域中使用。它被用来指定“非明确的值”。当你需要通过在这两个域中的一个来指定一些东西的时候,它是有用的。例如:月份中的日期和星期中的日期这两个元素时互斥的一起应该通过设置一个问号来表明不想设置那个字段。
  • -(短横杠):表示范围。例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次 。
  • /(斜杠): 表示起始时间开始触发,然后每隔固定时间触发一次。例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次。
  • ,(逗号): 表示列出枚举值。例如:在Minutes域使用5,20,则意味在520分钟的时候触发。
  • L : 表示最后,只能出现在DayofWeekDayofMonth域。如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。
  • W : 表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份 。
  • LW : 这两个字符可以连用,表达在某个月最后一个工作日,即最后一个星期五。
  • # : 用于确定每个月的第几个星期几,只能出现在DayofMonth域。

3.4. 常用实例

表达式 含义
0 0 12 * * ? 每天中午12点触发
0 30 12 ?* * 每天中午12点半触发
0 * 12 * * ? 在每天下午12:00到12:59之间每一分钟触发一次
0 0/10 12 * * ? 在12:00到12:59之间每10分钟触发一次
0 0/5 10,19 * * ? 在10:00-10:59和19:00-19:59之间每5分钟触发一次
0 0-30 10 * * ? 在10:00-10:30之间每一分钟触发一次
0 10,20,30 12 * * ? 每天12点的10分、20分、30分的时候触发一次
0 12,24 12 ? 3 WED 每年三月的星期三12:12和12:24触发
0 10 12 15 * ? 每月15号12:10触发一次
0 20 12 L * ? 每月最后一天的12:20触发
0 20 12 ?* 6L 每个月最后一个星期五12:20触发
0 20 12 ? * 6#3 每月的第三个星期五12:20触发

3.4. 对于CronTrigger触发器写法

3.4.1. 主要写法

每两秒钟触发一次

Trigger trigger = triggerBuilder.withIdentity("trigger-1", "group-1")
                    .startNow()
                    .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * * * ?"))
                    .build();
3.4.2. 其他方式

另外CronScheduleBuilder还提供了一些封装的方法,例如dailyAtHourAndMinuteatHourAndMinuteOnGivenDaysOfWeekweeklyOnDayAndHourAndMinute。下面看一下其中dailyAtHourAndMinute的使用:

Trigger trigger = triggerBuilder.withIdentity("trigger-1", "group-1")
                    .startNow()
                    .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(12, 20))
                    .build();

这种相当于时对上面cron表单式的封装,看一下源码就知道了:

public static CronScheduleBuilder dailyAtHourAndMinute(int hour, int minute) {
  DateBuilder.validateHour(hour);
  DateBuilder.validateMinute(minute);
	// 相当于每天某小时某分钟执行
  String cronExpression = String.format("0 %d %d ? * *", minute, hour);

  return cronScheduleNoParseException(cronExpression);
}

其他两种也一样,就不一一赘述。

四、参考文章

https://blog.csdn.net/wangmx1993328/article/details/105309522

https://blog.csdn.net/zhanglong_4444/article/details/104322354

上一篇:WPF 基础 - Trigger


下一篇:zabbix 部分内置变量