Quartz核心概念
- Scheduler调度器,一个调度器中可以注册多个JobDetail和Trigger,当Trigger与JobDetail组合,就可以被Scheduler容器调度了。
- Trigger 触发器,表示一个调度参数的配置,什么时候去执行
- JobDetail 表示一个具体可以执行的调度程序,Job 是这个可执行程调度程序所要执行的内容,另外 JobDetail 还包含了这个任务调度的方案和策略。
- Job 表示一个工作,要执行的具体内容。此接口中只有一个方法,如下:
public void execute(JobExecutionContext context)
1 调度器Scheduler
- Scheduler:调度器。就是quartz的大脑,所有任务都是由它来实施。包含两个重要的组件JobStore 和 ThreadPool。
JobStore 是存储运行时信息的,包括Trigger,Scheduler,JobDetail,业务锁等
ThreadPool就是线程池,Quartz有自己的线程池实现。所有任务都会由线程池执行。 - SchedulerFactory: Scheduler工厂,有两个实现类DirectSchedulerFactory 和StdSchedulerFactory。前者可以用来在代码里定制你自己的scheduler参数。后者是直接读取配置文件(如果没有配置,则采取默认的)来实例化Scheduler。SchedulerFactory本身是支持创建RMI stub的,可以用来管理远程的Scheduler,功能与本地一样。
2 触发器Trigger
2.1 SimpleTrigger
- 指定从某一个时间开始,以一定的时间间隔(单位是毫秒)执行的任务。
- 适合的任务类似于—【8:00开始,每隔1小时,执行1次】
- 属性包括repeatInterval(重复间隔);repeatCount (重复次数,实际执行次数是repeatCount+1,因为在startTime的时候一定会执行一次)
- 示例
SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1)//每隔一秒执行一次
.repeatForever() //一直执行
.build();
SimpleScheduleBuilder.simpleSchedule().withIntervalInMinutes(3)//每隔3分钟执行一次
.withRepeatCount(3) //执行3次
.build()
2.2 CalendarIntervalTrigger
指定从某一个时间开始,以一定的时间间隔执行的任务,类似于SimpleTrigger。二者的区别是:
SimpleTrigger指定的时间间隔为毫秒,没办法指定每隔一个月执行一次(每个月的时间间隔不是固定值);CalendarIntervalTrigger支持的间隔单位有秒,分钟,小时,天,月,年,星期。
CalendarIntervalScheduleBuilder.calendarIntervalSchedule().withIntervalInDays(2).build();
2.3 DailyTimeIntervalTrigger
- 指定每天的某个时间段内,以一定的时间间隔执行任务。并且可以指定星期
- 适合任务-【每天8:00 -15:00,每隔2分钟执行一次,并且只要求周六执行】
- 属性有:startTimeOfDay 每天开始时间;endTimeOfDay 每天结束时间;daysOfWeek 需要星期几执行;interval时间间隔;intervalUnit 执行间隔的单位(秒,分钟,小时,天,月,年,星期);repeatCount 重复次数。
- 示例
DailyTimeIntervalScheduleBuilder.dailyTimeIntervalSchedule()
.startingDailyAt(TimeOfDay.hourAndMinuteOfDay(8, 0))//每天8点开始
.endingDailyAt(TimeOfDay.hourAndMinuteOfDay(15, 0))//每天15点结束
.onDaysOfTheWeek(Calendar.SATURDAY)//星期六执行
.withIntervalInHours(1)//每一个小时执行1次
.withRepeatCount(100)//执行100次
.build();
2.4 CronTrigger(常用)
- 适合更复杂的任务,基本上覆盖了以上三个Trigger的绝大部分功能。
- 适合任务-【每天0点,6点各执行一次】
- 属性-【cron表达式】
- 示例:
CronScheduleBuilder.cronSchedule("0 0/2 10-12 * * ?").build();//每天10点-12点 每隔2分钟执行一次
2.4.1 cron表达式
位置 | 时间域 | 允许值 | 特殊值 |
---|---|---|---|
1 | 秒 | 0-59 | ,-*/ |
2 | 分钟 | 0-59 | ,-*/ |
3 | 小时 | 0-23 | ,-*/ |
4 | 日期 | 1-31 | ,-*/?LW |
5 | 月份 | 1-12 | ,-*/ |
6 | 星期 | 1-7 | ,-*/?L# |
7 | 年份(可选) | 1970~2099 | ,-*/ |
- 星号(*) :所有字段均可用,表示对应时间域的每一个时刻。在秒字段,就表示“每秒”
- 问号(?) :只在日期和星期两个字段中使用,表示“不确定值”。例如想在每月的1日触发调度,不管1日到底是星期几,则只能使用如下写法: 13 13 15 1 * ?, 其中最后一位只能用?,而不能使用*
- 减号(-):表示范围。例如在分钟字段使用5-20,表示从5分到20分
- 逗号(,):表示列出枚举值。例如在日期字段使用“1,3,5”,则表示每月1日,3日,5日
- 斜线(/):表示起始时间开始触发,然后每隔固定时间触发一次。例如在分钟字段使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次. 也可以使用*/y,等同于0/y。
- L:表示最后,只能出现在日期和星期字段。L如果在日期字段中,表示这个月份的最后一天,如1月的31日,非闰年的2月的28日。如果用在星期中,则表示星期六,但是如果在星期字段使用5L,意味着在最后的一个星期四触发。
- W:只能出现在日期字段,表示有效工作日(周一到周五),系统将在离指定日期的最近的有效工作日触发事件。例如:在 日期字段使用12W,如果12日是星期六,则将在最近的工作日:星期五,即11日触发。如果12日是星期天,则在13日(周一)触发;如果12日在星期一到星期五中的一天,则就在12日触发。另外一点,W的最近寻找不会跨过月份 。比如,指定1W,如果1日是星期六,则在3日出发,而不是上个月的最后那天。W只能指定单一日期,不能指定日期范围
- LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
- #:用于确定每个月第几个星期几,只能出现在日期字段。例如在4#2,表示某月的第二个星期三。
一个在线校验cron表达式的网址
3 Job并发
Job是有肯能并发执行的,比如一任务要执行10秒中才能结束,而调度算法是每秒中触发一次,那么就有可能过个任务被并发执行。
但是有时候我们并不想任务并发执行,比如有个任务要去“获得数据库中多有未发送邮件的名单”,如果是并发执行,就需要一个数据库锁去避免一个数据被多次处理。注解DisallowConcurrentExecution就可以解决这个问题。该注解对JobDetail实例生效,如果你定义两个JobDetail,引用同一个Job类,是可以并发执行的。
@DisallowConcurrentExecution
public class MyJob implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
try {
Thread.sleep(4000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//创建任务详情
JobDetail jobDetail = context.getJobDetail();
String name = jobDetail.getKey().getName();//任务名
String group = jobDetail.getKey().getGroup();//任务group
String jobData = jobDetail.getJobDataMap().getString("data");//任务中的数据
System.out.println("job's name: "+ name + " group: " + group + " data: " + jobData + " " + new Date());
}
}
@Test
public void testCronTrigger() {
try {
//1. 创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2. 定义一个Trigger,触发条件类
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")//定义name/group
.withSchedule(CronScheduleBuilder.cronSchedule("*/2 * * * * ?"))
.build();
//3. 定义一个JobDetail
//定义Job类为Myjob类,这是真正的执行逻辑
JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity("test1","test")//name and group
.usingJobData("data","Hello")//定义属性,存储数据
.build();
//4. 调度器中加入任务和触发器
scheduler.scheduleJob(job, trigger);
//5. 启动调度器
scheduler.start();
Thread.sleep(600000);
scheduler.shutdown();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
来源:CSDN
作者:香蜜沉沉烬如霜
链接:https://blog.csdn.net/yiyuanyu/article/details/104295496