Quartz是OpenSymphony
开源组织在Job scheduling
领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer
。但是相较于Timer, Quartz增加了很多功能:
持久性作业 - 就是保持调度定时的状态;
作业管理 - 对调度作业进行有效的管理;
官方文档:
http://www.quartz-scheduler.org/documentation/
http://www.quartz-scheduler.org/api/2.3.0/index.html
Quartz 的核心类由以下三部分构成:
任务 Job : 需要实现的任务类,实现 execute()
方法,执行后完成任务。
触发器 Trigger : 包括 SimpleTrigger
和 CronTrigger
。
调度器 Scheduler : 任务调度器,负责基于 Trigger
触发器,来执行 Job任务。
关系结构图:
第一步:创建Maven项目,添加Quartz 定时任务框架依赖Jar包
-
- <dependency>
- <groupId>org.quartz-schedulergroupId>
- <artifactId>quartzartifactId>
- <version>2.3.0version>
- dependency>
-
- <dependency>
- <groupId>org.quartz-schedulergroupId>
- <artifactId>quartz-jobsartifactId>
- <version>2.3.0version>
- dependency>
第二步:自定义Job
- package com.zzg.quartz.job;
-
- import org.quartz.Job;
- import org.quartz.JobExecutionContext;
- import org.quartz.JobExecutionException;
-
- public class MyJob implements Job {
- @Override
- public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
- System.out.println("定时任务执行了!");
- }
- }
第三步:测试方法实现创建调度器、jobDetail 实例、trigger 实例、执行。
- package com.zzg.quartz;
-
- import com.zzg.quartz.job.MyJob;
- import org.quartz.*;
- import org.quartz.impl.StdSchedulerFactory;
-
- import java.util.concurrent.TimeUnit;
-
- import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
-
- /**
- * 核心步骤说明:
- * 1.创建调度器 Scheduler
- *
- * 2.创建JobDetail实例,并与MyJob类绑定
- *
- * 3.构建Trigger实例,指定时间执行
- *
- * 4.执行,开启调度器
- */
- public class QuartzTest {
- public static void main(String[] args) throws SchedulerException, InterruptedException {
- // 1.创建调度器 Scheduler
- SchedulerFactory factory = new StdSchedulerFactory();
- Scheduler scheduler = factory.getScheduler();
-
- // 2.创建JobDetail实例,并与MyJob类绑定(Job执行内容)
- JobDetail job = JobBuilder.newJob(MyJob.class)
- .withIdentity("job1", "group1")
- .build();
-
- // 3.构建Trigger实例,每隔30s执行一次
- Trigger trigger = TriggerBuilder.newTrigger()
- .withIdentity("trigger1", "group1")
- .startNow()
- .withSchedule(simpleSchedule()
- .withIntervalInSeconds(30)
- .repeatForever())
- .build();
-
- // 4.执行,开启调度器
- scheduler.scheduleJob(job, trigger);
- System.out.println(System.currentTimeMillis());
- scheduler.start();
-
- //主线程睡眠1分钟,然后关闭调度器
- TimeUnit.MINUTES.sleep(1);
- scheduler.shutdown();
- System.out.println(System.currentTimeMillis());
- }
- }
JobDetail 的作用是绑定 Job,是一个任务实例,它为 Job 添加了许多扩展参数。
主要字段 | 涵义、作用 |
---|---|
name | 任务名称 |
group | 任务分组,默认分组 DEFAULT |
jobClass | 任务类,就是上面 Demo 中的 MyJob 的路径 |
jobDataMap | 任务参数信息。JobDetail、Trigger 都可以使用 JobDataMap 来设置一些参数或信息。 |
每次Scheduler
调度执行一个Job的时候,首先会拿到对应的Job,然后创建该Job实例,再去执行Job中的execute()
的内容,任务执行结束后,关联的Job对象实例会被释放,且会被JVM GC清除。
疑问:为什么设计成JobDetail + Job,不直接使用Job
JobDetail 定义的是任务数据,而真正的执行逻辑是在Job中。
这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而
JobDetail & Job
方式,Sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以 规避并发访问 的问题。
当 Scheduler
调用一个 job,就会将 JobExecutionContext
传递给 Job 的 execute()
方法;
Job 能通过 JobExecutionContext
对象访问到 Quartz 运行时候的环境以及 Job 本身的明细数据。
任务实现的 execute()
方法,可以通过 context 参数获取。
- public interface Job {
- void execute(JobExecutionContext context)
- throws JobExecutionException;
- }
在 JobDetail 建造过程中,可以使用如下方法:
- // 2.创建JobDetail实例,并与MyJob类绑定(Job执行内容)
- JobDetail job = JobBuilder.newJob(MyJob.class)
- .withIdentity("job1", "group1")
- // 定义任务添加自定义参数
- .usingJobData("quartz", "quartz 自定义参数")
- .build();
在 Trigger建造过程中,可以使用如下方法:
- // 3.构建Trigger实例,每隔30s执行一次
- Trigger trigger = TriggerBuilder.newTrigger()
- .withIdentity("trigger1", "group1")
- // Trigger 添加自定义参数
- .usingJobData("trigger", "trigger 自定义参数")
- .startNow()
- .withSchedule(simpleSchedule()
- .withIntervalInSeconds(30)
- .repeatForever())
- .build();
在 自定义MyJob中的execute
方法中获取:
- package com.zzg.quartz.job;
-
- import org.quartz.Job;
- import org.quartz.JobExecutionContext;
- import org.quartz.JobExecutionException;
-
- public class MyJob implements Job {
- @Override
- public void execute(JobExecutionContext context) throws JobExecutionException {
- Object group = context.getTrigger().getJobDataMap().get("trigger");
- Object group1 = context.getJobDetail().getJobDataMap().get("quartz");
- System.out.println("定时任务执行了!");
- }
- }
有状态的 job 可以理解为多次 job调用期间可以持有一些状态信息,这些状态信息存储在 JobDataMap
中。
而默认的无状态 job,每次调用时都会创建一个新的 JobDataMap
。
自定义MyJob
- //多次调用 Job 的时候,将参数保留在 JobDataMap
- @PersistJobDataAfterExecution
- public class JobStatus implements Job {
- @Override
- public void execute(JobExecutionContext context) throws JobExecutionException {
- long count = (long) context.getJobDetail().getJobDataMap().get("count");
- System.out.println("当前执行,第" + count + "次");
- context.getJobDetail().getJobDataMap().put("count", ++count);
- }
- }
在 JobDetail 建造过程中,携带拓展参数
- JobDetail job = JobBuilder.newJob(JobStatus.class)
- .withIdentity("statusJob", "group1")
- .usingJobData("count", 1L)
- .build();
Trigger
可以设置任务的开始结束时间, Scheduler
会根据参数进行触发。
- Calendar instance = Calendar.getInstance();
- Date startTime = instance.getTime();
- instance.add(Calendar.MINUTE, 1);
- Date endTime = instance.getTime();
-
- // 3.构建Trigger实例
- Trigger trigger = TriggerBuilder.newTrigger()
- .withIdentity("trigger1", "group1")
- // 开始时间
- .startAt(startTime)
- // 结束时间
- .endAt(endTime)
- .build();
在 job 中也能拿到对应的时间,并进行业务判断
- public void execute(JobExecutionContext context) throws JobExecutionException {
- System.out.println("任务执行。。。");
- System.out.println(context.getTrigger().getStartTime());
- System.out.println(context.getTrigger().getEndTime());
- }
比较简单的一类触发器,用它能实现很多基础的应用。使用它的主要场景包括:
在指定时间段内,执行一次任务
最基础的 Trigger 不设置循环,设置开始时间。
在指定时间段内,循环执行任务
在 快速入门代码基础上加上循环间隔。可以指定 永远循环、运行指定次数
- TriggerBuilder.newTrigger()
- .withSchedule(SimpleScheduleBuilder
- .simpleSchedule()
- .withIntervalInSeconds(30)
- .repeatForever())
- withRepeatCount(count)` 是重复次数,实际运行次数为 `count+1
- TriggerBuilder.newTrigger()
- .withSchedule(SimpleScheduleBuilder
- .simpleSchedule()
- .withIntervalInSeconds(30)
- .withRepeatCount(5))
这个省略...
CronTrigger
是基于日历的任务调度器,在实际应用中更加常用。
虽然很常用,但是知识点都一样,只是可以通过表达式来设置时间而已。
使用方式就是绑定调度器时换一下:
TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ?"))
Cron 表达式请参考:Cron表达式学习
请参考:Spring Boot2.x + Quartz 定时任务模块