jobDataMap
jobDataMap可以用来装载可序列化的对象
,用于在job执行的时候获取。
注:jobDetail
和trigger
中都可以添加jobDataMap,后面的对象会把前面对象相同键值对象的值覆盖
。
job实体类创建代码(创建job示例,添加jobDataMap)
public static void main(String[] args) throws SchedulerException, ParseException { SchedulerFactory schedulerFactory = new StdSchedulerFactory(); Scheduler scheduler = schedulerFactory.getScheduler(); scheduler.start(); // 定义job,绑定我们的定时任务 JobDetail job2 = newJob(HelloJob.class) .usingJobData("name", "李四") .usingJobData("characteristic", "22") .build(); // 执行任务,用定义好的触发器 和 任务 scheduler.scheduleJob(job2, getTrigger1()); }
job类代码(job运行示例,获取jobDataMap)
public void execute(JobExecutionContext context) throws JobExecutionException { JobDataMap jobDataMap = context.getJobDetail().getJobDataMap(); String name = jobDataMap.get("name").toString(); String characteristic = jobDataMap.get("characteristic").toString(); JobDataMap mergedJobDataMap = context.getMergedJobDataMap(); String mergedName = mergedJobDataMap.getString("name"); System.out.println("name : " + name); System.out.println("mergedName:" + mergedName); System.out.println("characteristic : " + characteristic); }
trigger
/** * 触发器立即触发,然后每隔2秒 触发一次,22:55:00: */ private static SimpleTrigger getTrigger1() { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //定义一个任务触发器 return TriggerBuilder.newTrigger() .withIdentity("job2", "group2") //定点触发 //.startAt(sdf.parse("2018-09-27 10:27:00")) // 五秒钟后触发 .startAt(DateBuilder.futureDate(5, DateBuilder.IntervalUnit.SECOND)) .withSchedule( simpleSchedule() .withIntervalInSeconds(2) .repeatForever() //.withRepeatCount(0) ) .usingJobData("name",",,") //.endAt(DateBuilder.dateOf(22, 55, 0)) .build(); }
测试结果:
name : 李四mergedName:,,characteristic : 22
trigger
trigger是触发器,任务何时运行、运行几次
,它说了算。
TriggerKey
- TriggerKey:triggerKey是所有触发器的标识信息。
- JobKey: job示例的表示信息,触发器被触发时,该指定的job示例会运行
TriggerKey介绍
设置TriggerKey可以通过JobBuilder的.withIdentity()
进行设置,如下:默认api提供三种方式 <img src="https://ws3.sinaimg.cn/mw690/005AQjvJly1fw41dc8fvjj30s608wq4z.jpg" alt="image">
全参数的源码如下:
public TriggerBuilderwithIdentity(String name, String group) { key = new TriggerKey(name, group); return this;}
注:name属性是必填的,group不写,默认值为:default
JobKey大同小异,不予介绍。
其他通用属性
- startTime:启动时间,默认当前时间,立即执行
- endTime:自行设置,默认为空
- priority: 优先级,默认为5,用于多个Trigger并发执行。
simpleTrigger
使用场景:在一个指定时间段内执行一次作业任务
或是在指定的时间间隔内多次执行作业任务
,SimpleTrigger应该能满足你的调度需求。
例如:
- 你希望触发器在2015年1月13日上午11:23:54准时触发,或是希望在那个时间点触发,然后再重复触发5次,每隔10秒一次。
使用案例:
- 触发器 2018-09-27 10:27:00 定点触发,重复0次
private static SimpleTrigger getTrigger2() throws ParseException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); //定义一个任务触发器 return newTrigger() .withIdentity("job1", "group1") //定点触发 .startAt(sdf.parse("2018-09-27 10:27:00")) .withSchedule(simpleSchedule() .withIntervalInSeconds(2) .withRepeatCount(0) ).build(); }
注:startAt中的日期可以使用DateBuilder,示例,在指定时间延迟5秒执行
.startAt(DateBuilder.futureDate(5, DateBuilder.IntervalUnit.SECOND))
触发失败指令(待完善。。。)
执行常量如下:
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MISFIRE_INSTRUCTION_FIRE_NOW MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_REMAINING_REPEAT_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_REMAINING_COUNT MISFIRE_INSTRUCTION_RESCHEDULE_NEXT_WITH_EXISTING_COUNT
触发指令设置
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值
cronTrigger
使用场景:CronTrigger比SimpleTrigger更常用,当你需要一个基于日历般概念的作业调度器,而不是像SimpleTrigger那样精确指定间隔时间。
使用cronTrigger,你可以这样指定触发时间表例如每周五的中午
,或是每周末的上午9:30
,甚至是一月份每周一、三、五上午9:00到10:00之间每5分钟
。
CronTrigger也需要指定startTime让调度器生效,指定endTime让调度器终止。
cron表达式
Cron表达式实际上是由7个子表达式组成的字符串,这些子表达式用空格隔开,分别代表:
类型 | 范围 |
---|---|
秒 | 0-60 |
分 | 0-60 |
小时 | 0-23 |
月份中的天数 | 0-31,但需要注意,有些月份如没有31 |
月 | 0-11,可以使用:JAN,FEB,MAR,APR,MAY,JUN,JUL,AUG,SEP,OCT,NOV,DEC |
星期中的天数 | 1-7,可以使用: SUN,MON,TUE,WED,THU,FRI,SAT |
年(可选) | 一般不用 |
符号介绍:
/ : - 如果你在分钟字段写“0/15”,这表示“每次从一小时中的第0分钟开始,每隔15分钟触发”, - 如果你在分钟字段上写“3/20”,这表示“每次从一小时中的第3分钟开始,每隔20分钟触发”——换句话说,这跟在分钟字段上指定“3,23,43”是一样的。
? : - 字符允许出现在月份中的天数和星期中的天数字段中。它一般用来指定“不关心的值”。 - 当你需要在这两个字段中的一个指定不确定的值是非常方便的,这个字符不能用在其他的字段中。
L : - 字符允许出现在月份中的天数和星期中的天数字段中 - “L”字符出现在月份中的天数字段中表示“每月的最后一天”——1月31日,平年的2月28日
w : - 字符用来指定给定日期的最近一个工作日(工作日指的是从周一到周五) - 如果你在月份中的天数字段的值指定为“15w”,这表示“离每月15号最近的工作日”。
'井' : - ”字符用来指定每月的第N个工作日 - 星期中的天数字段的值为“6#3”或是“FRI#3”表示“每月的第三个星期五”。
常用表达式案例
Cron案例1——仅仅表示每隔5分钟触发一次:"0 0/5 * * * ?"
Cron案例2——表示每隔5分钟,在过了10秒后触发一次(例如上午10:00:10,10:05:10等):"10 0/5 * * * ?"
Cron案例3——表示每个周三到周五,在上午10:30,11:30,12:30和13:30分触发:"0 30 10-13 ? * WED,FRI"
Cron案例4——表示每月从5号到20号,上午8时到10时之间的每半小时触发,注意这个触发器只在8:00,8:30,9:00和9:30分触发,上午10:00不会触发:"0 0/30 8-9 5,20 * ?"
注意有些调度需求因太复杂例如“上午9:00到10:00之间的每5分钟,下午1:00到10:00的每20分钟”,而不能用单一的触发器来表示。这种情况的解决方案是创建两个简单的触发器,将它们注册到调度器中去运行同一个作业任务。
代码实现(CronTrigger构建)
触发器,每天从的下午3:33 触发一次
private static CronTrigger getTrigger5() throws ParseException { //定义一个任务触发器 return newTrigger() .withIdentity("job1", "group1") .withSchedule(CronScheduleBuilder.cronSchedule("0 27 16 * * ?")) //或者下边这样写 // .withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(15, 33)) .forJob("job1", "group1") .build(); }
注:CronScheduleBuilder默认提供了一些常用表达式实现,如下图,请自行尝试
<img src="https://ws2.sinaimg.cn/mw690/005AQjvJly1fw440fu2jnj31dw0fowkp.jpg" alt="image">
触发失败指令(待完善。。。)
CronTrigger的触发失败指令常量:
MISFIRE_INSTRUCTION_IGNORE_MISFIRE_POLICY MISFIRE_INSTRUCTION_DO_NOTHING MISFIRE_INSTRUCTION_FIRE_NOW
触发指令设置:
withMisfireHandlingInstructionDoNothing——不触发立即执行——等待下次Cron触发频率到达时刻开始按照Cron频率依次执行withMisfireHandlingInstructionIgnoreMisfires——以错过的第一个频率时间立刻开始执行——重做错过的所有频率周期后——当下一次触发频率发生时间大于当前时间后,再按照正常的Cron频率依次执行withMisfireHandlingInstructionFireAndProceed——以当前时间为触发频率立刻触发一次执行——然后按照Cron频率依次执行
暂停及恢复补全问题
问题介绍: - 1).执行暂停任务,重启后,在暂停期间没执行的任务又TM给补上了 - 2).服务器挂了,重启后,在暂停期间没执行的任务又TM给补上了
解决方式:
- 1).先设置触发失败指令
cronSchedule(cronExpression).withMisfireHandlingInstructionFireAndProceed() 或者 cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing()
设置完成重新启动发现没软用
- 2).配置参数 quartz.properties中
#这个时间大于10000(10秒)会导致MISFIRE_INSTRUCTION_DO_NOTHING不起作用。org.quartz.jobStore.misfireThreshold = 5000
解释:
misfireThreshold表示实际执行的时间与下一次应该执行时间之间的差值,超过这个差值就不会执行,低于这个差值就会执行。 比如我每3秒执行一次,misfireThreshold=6000,当你暂停低于6秒内,它会弥补执行,超过6秒,它就不再弥补执行了。
参见:
(quartz暂停及恢复任务解决恢复时一咕噜把未执行的全补回来的问题)[]<br> (quartz 失败触发指令)[]<br>