依赖

1
2
3
4
5
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.1</version>
</dependency>

基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//实际触发时间。例如,计划时间可能是 10:00:00,但如果调度程序太忙,实际触发时间可能是 10:00:03。
Date fireTime = jobExecutionContext.getFireTime();
System.out.println("fireTime:" + fireTime);
//上次触发时间
Date previousFireTime = jobExecutionContext.getPreviousFireTime();
System.out.println("previousFireTime:" + previousFireTime);
//下次触发时间
Date nextFireTime = jobExecutionContext.getNextFireTime();
System.out.println("nextFireTime:" + nextFireTime);
//触发器触发的预定时间。
Date scheduledFireTime = jobExecutionContext.getScheduledFireTime();
System.out.println("scheduledFireTime:" + scheduledFireTime);

JobDetail jobDetail = jobExecutionContext.getJobDetail();
System.out.println("jobDataMap:" + JSON.toJSONString(jobDetail.getJobDataMap()));
System.out.println("jobKey:" + JSON.toJSONString(jobDetail.getKey()));
System.out.println("jobDescription:" + jobDetail.getDescription());
System.out.println("==================================");
}

public static void main(String[] args) throws SchedulerException {
//1.创建一个jobDetail的实例,将该实例与HelloJob Class绑定
JobDetail jobDetail = JobBuilder
.newJob(HelloJob.class) //定义Job类为HelloJob类,真正的执行逻辑所在
.withIdentity("hello","group1")
.withDescription("Quartz测试")
.usingJobData("name", "小米")
.usingJobData("age", 15)
.build();

//2.创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startNow() //立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2) //每2秒执行一次
.repeatForever()) //一直执行
.build();
//3.创建schedule实例
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.start(); //启动
scheduler.scheduleJob(jobDetail, trigger); // jobDetail和trigger加入调度
}
}

JobExecutionContext

当Scheduler调用一个Job就会将JobExecutionContext传递给Job的execute()方法,Job能通过JobExecutionContext对象访问到Quartz运行时候的环境和Job本身的明细数据

JobDetail、JobBuilder

方法

storeDurably

JobDetails信息持久化到数据库的时候有一个属性storeDurably,如果设置为true则无论与其关联的Trigger是否存在其都会一直存在,否则只要相关联的trigger删除掉了其会自动删除掉

requestRecovery

请求恢复,也就是说当应用发生故障的时候,是否重新执行默认是false。如果一个job是可恢复的,并且在其执行的时候,scheduler发生硬关闭(hard shutdown)(比如运行的进程崩溃了,或者关机了),则当scheduler重新启动的时候,该job会被重新执行。此时,该job的JobExecutionContext.isRecovering() 返回true

usingJobData、setJobData

添加Job数据,每个JobDetail内都有一个JobDataMap,包含了关联到这个Job的数据,在Job类中,可以通过context取出该数据,进行业务流程处理。

withIdentity

给JobDetail起一个Id,方便后面检索

withDescription

用来对job进行描述,并没有什么实际作用

JobKey

JobKey是表明Job身份的一个对象,里面封装了Job的name和group,TriggerKey同理。当不指定group时,Quartz会用默认的组名DEFAULT

JobDataMap

JobDetail是任务的定义,而Job是任务的执行逻辑,每一个JobDetail都会有一个JobDataMap,JobDataMap本质就是一个Map的扩展类,可以存储一些任务信息

JobDataMap获取任务信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class HelloJob implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
JobDetail jobDetail = jobExecutionContext.getJobDetail();
JobDataMap jobDataMap = jobDetail.getJobDataMap();
System.out.println("name:" + jobDataMap.getString("name"));
System.out.println("age:" + jobDataMap.getInt("age"));
System.out.println("jobKey:" + JSON.toJSONString(jobDetail.getKey()));
System.out.println("jobDescription:" + jobDetail.getDescription());
System.out.println("==================================");
}

public static void main(String[] args) throws SchedulerException {
//1.创建一个jobDetail的实例,将该实例与HelloJob Class绑定
JobDetail jobDetail = JobBuilder
.newJob(HelloJob.class) //定义Job类为HelloJob类,真正的执行逻辑所在
.withIdentity("hello","group1")
.withDescription("Quartz测试")
.usingJobData("name", "小米")
.usingJobData("age", 15)
.build();

//2.创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startNow() //立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2) //每2秒执行一次
.repeatForever()) //一直执行
.build();
//3.创建schedule实例
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.start(); //启动
scheduler.scheduleJob(jobDetail, trigger); // jobDetail和trigger加入调度
}
}

实体类获取任务信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class HelloJob3 implements Job {

private String message;
private Float floatJobValue;
private Double doubleTriggerValue;

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public Float getFloatJobValue() {
return floatJobValue;
}

public void setFloatJobValue(Float floatJobValue) {
this.floatJobValue = floatJobValue;
}

public Double getDoubleTriggerValue() {
return doubleTriggerValue;
}

public void setDoubleTriggerValue(Double doubleTriggerValue) {
this.doubleTriggerValue = doubleTriggerValue;
}

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
//打印当前的执行时间 例如 2017-11-22 00:00:00
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("现在的时间是:" + sf.format(date));
System.out.println("jobDataMap定义的message的值 : " + message); //打印jobDataMap定义的message的值
System.out.println("jobDataMap定义的floatJobValue的值 : " + floatJobValue); //jobDataMap定义的floatJobValue的值
System.out.println("==================================");
}

public static void main(String[] args) throws SchedulerException {
//1.创建一个jobDetail的实例,将该实例与HelloJob Class绑定
JobDetail jobDetail = JobBuilder
.newJob(HelloJob3.class) //定义Job类为HelloJob类,真正的执行逻辑所在
.withIdentity("myJob", "group1") //定义name 和 group
.usingJobData("message","hello myJob1") //加入属性到jobDataMap
.usingJobData("FloatJobValue",8.88f) //加入属性到jobDataMap
.build();

//2.创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startNow() //立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2) //每2秒执行一次
.repeatForever()) //一直执行
.build();
//3.创建schedule实例
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.start(); //启动
scheduler.scheduleJob(jobDetail,trigger); // jobDetail和trigger加入调度
}
}

注解

@PersistJobDataAfterExecution

有状态的Job可以理解为多次Job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中,而默认的无状态Job每次调用时都会创建一个新的JobDataMap

注意:没有添加@PersistJobDataAfterExecution注解,每次调用时都会创建一个新的JobDataMap,不会累加;添加该注解后,多次调用期间可以持有一些状态信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@PersistJobDataAfterExecution
public class HelloJob4 implements Job {

private Integer count;

public void setCount(Integer count) {
this.count = count;
}

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
System.out.println(++count);
jobExecutionContext.getJobDetail().getJobDataMap().put("count", count);
}

public static void main(String[] args) throws SchedulerException {
//1.创建一个jobDetail的实例,将该实例与HelloJob Class绑定
JobDetail jobDetail = JobBuilder
.newJob(HelloJob4.class) //定义Job类为HelloJob类,真正的执行逻辑所在
.withIdentity("myJob", "group1") //定义name 和 group
.usingJobData("count",0) //加入属性到jobDataMap
.build();

//2.创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
SimpleTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity("myTrigger", "group1")
.startNow() //立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2) //每2秒执行一次
.repeatForever()) //一直执行
.build();
//3.创建schedule实例
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.start(); //启动
scheduler.scheduleJob(jobDetail,trigger); // jobDetail和trigger加入调度
}
}

@DisallowConcurrentExecution

禁止并发执行多个相同定义的JobDetail,这个注解是加在Job类上的,但意思并不是不能同时执行多个Job,而是不能并发执行同一个Job

例如:同一个Job实现类DemoJob的两个JobDetail实例A和B,设置A的定时执行频率为每1分钟执行一次,A的实际运行耗时为3分钟,B的定时执行频率也是每1分钟执行一次,B的实际运行耗时为30秒。假如在07:00分00秒时A和B同时第一次运行,则到07:00分30秒时B运行结束,此时A还在运行中,到07:01分00秒时A和B又该执行了,但是由于注解@DisallowConcurrentExecution的缘故,此时A不会再次运行,A只能在其上一次运行结束后才能再次被调用执行。但是B会正常运行(B不受A的影响,注解@DisallowConcurrentExecution是作用于JobDetail实例而不是Job实现类)

注意:如果你使用了@PersistJobDataAfterExecution注解,则强烈建议你同时使用@DisallowConcurrentExecution注解,因为当同一个job(JobDetail)的两个实例被并发执行时,由于竞争,JobDataMap中存储的数据很可能是不确定的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
@DisallowConcurrentExecution
public class HelloJob5 implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("现在的时间是:" + sf.format(date));
JobKey key = jobExecutionContext.getJobDetail().getKey();
//打印jobDetail 的name
System.out.println("jobDetail 的name : " + key.getName());
//打印jobDetail 的group
System.out.println("jobDetail 的group : " + key.getGroup());
System.out.println("==============================");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) throws SchedulerException {
//1.创建一个jobDetail的实例,将该实例与HelloJob Class绑定
JobDetail jobDetail1 = JobBuilder
.newJob(HelloJob5.class) //定义Job类为HelloJob类,真正的执行逻辑所在
.withIdentity("group1", "group1") //定义name 和 group
.build();

//2.创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
SimpleTrigger trigger1 = TriggerBuilder.newTrigger()
.withIdentity("myTrigger1", "group1")
.startNow() //立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2) //每2秒执行一次
.repeatForever()) //一直执行
.build();
//3.创建schedule实例
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.start(); //启动
scheduler.scheduleJob(jobDetail1, trigger1); // jobDetail和trigger加入调度
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
// 运行结果:
现在的时间是:2024-11-12 00:59:49
jobDetail 的name : group1
jobDetail 的group : group1
==============================
现在的时间是:2024-11-12 00:59:52
jobDetail 的name : group1
jobDetail 的group : group1
==============================
现在的时间是:2024-11-12 00:59:55
jobDetail 的name : group1
jobDetail 的group : group1
==============================

注释@DisallowConcurrentExecution后

1
2
3
4
5
6
7
8
9
10
11
12
13
// 运行结果:
现在的时间是:2024-11-12 01:00:48
jobDetail 的name : group1
jobDetail 的group : group1
==============================
现在的时间是:2024-11-12 01:00:50
jobDetail 的name : group1
jobDetail 的group : group1
==============================
现在的时间是:2024-11-12 01:00:52
jobDetail 的name : group1
jobDetail 的group : group1
==============================

创建两个Job对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
@DisallowConcurrentExecution
public class HelloJob5 implements Job {

@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
Date date = new Date();
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("现在的时间是:" + sf.format(date));
JobKey key = jobExecutionContext.getJobDetail().getKey();
//打印jobDetail 的name
System.out.println("jobDetail 的name : " + key.getName());
//打印jobDetail 的group
System.out.println("jobDetail 的group : " + key.getGroup());
System.out.println("==============================");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}

public static void main(String[] args) throws SchedulerException {
//1.创建一个jobDetail的实例,将该实例与HelloJob Class绑定
JobDetail jobDetail1 = JobBuilder
.newJob(HelloJob5.class) //定义Job类为HelloJob类,真正的执行逻辑所在
.withIdentity("group1", "group1") //定义name 和 group
.build();

//2.创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
SimpleTrigger trigger1 = TriggerBuilder.newTrigger()
.withIdentity("myTrigger1", "group1")
.startNow() //立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2) //每2秒执行一次
.repeatForever()) //一直执行
.build();
//3.创建schedule实例
StdSchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.start(); //启动
scheduler.scheduleJob(jobDetail1, trigger1); // jobDetail和trigger加入调度

JobDetail jobDetail2 = JobBuilder
.newJob(HelloJob5.class) //定义Job类为HelloJob类,真正的执行逻辑所在
.withIdentity("group2", "group2") //定义name 和 group
.build();

//2.创建一个Trigger触发器的实例,定义该job立即执行,并且每2秒执行一次,一直执行
SimpleTrigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("myTrigger2", "group2")
.startNow() //立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3) //每3秒执行一次
.repeatForever()) //一直执行
.build();
//3.创建schedule实例
Scheduler scheduler2 = factory.getScheduler();
scheduler2.start(); //启动
scheduler2.scheduleJob(jobDetail2, trigger2); // jobDetail和trigger加入调度
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 运行结果
现在的时间是:2024-11-12 01:08:37
现在的时间是:2024-11-12 01:08:37
jobDetail 的name : group2
jobDetail 的name : group1
jobDetail 的group : group1
jobDetail 的group : group2
==============================
==============================
现在的时间是:2024-11-12 01:08:40
jobDetail 的name : group2
jobDetail 的group : group2
==============================
现在的时间是:2024-11-12 01:08:40
jobDetail 的name : group1
jobDetail 的group : group1
==============================