更多精彩关注掌上编程公众号
一、什么是Quartz
Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,
可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:
1. 持久性作业 - 就是保持调度定时的状态;
2. 作业管理 - 对调度作业进行有效的管理;
大部分公司都会用到定时任务这个功能。
拿火车票购票来说,当你下单后,后台就会插入一条待支付的task(job),一般是30分钟,超过30min后就会执行这个job,去判断你是否支付,未支付就会取消此次订单;当你支付完成之后,后台拿到支付回调后就会再插入一条待消费的task(job),Job触发日期为火车票上的出发日期,超过这个时间就会执行这个job,判断是否使用等。
在我们实际的项目中,当Job过多的时候,肯定不能人工去操作,这时候就需要一个任务调度框架,帮我们自动去执行这些程序。那么该如何实现这个功能呢?
(1)首先我们需要定义实现一个定时功能的接口,我们可以称之为Task(或Job),如定时发送邮件的task(Job),重启机器的task(Job),优惠券到期发送短信提醒的task(Job),实现接口如下:
(2)有了任务之后,还需要一个能够实现触发任务去执行的触发器,触发器Trigger最基本的功能是指定Job的执行时间,执行间隔,运行次数等。
(3)有了Job和Trigger后,怎么样将两者结合起来呢?即怎样指定Trigger去执行指定的Job呢?这时需要一个Schedule,来负责这个功能的实现。
上面三个部分就是Quartz的基本组成部分:
1. 调度器:Scheduler
2. 任务:JobDetail
3. 触发器:Trigger,包括SimpleTrigger和CronTrigger
二、Quartz Demo搭建
下面来利用Quartz搭建一个最基本的Demo。
1、导入依赖的jar包:
<!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
2、新建一个能够打印任意内容的Job:
/**
* @author 2460798168@qq.com
* @date 2019/12/3 11:36
*/
public class DemoJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
String printTime = new SimpleDateFormat("yy-MM-dd-HH-mm-ss").format(new Date());
System.out.println("PrintWordsJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100));
}
}
3、创建Schedule,执行任务:
/**
* @author 2460798168@qq.com
* @date 2019/12/3 11:40
*/
public class DemoSchedule {
public static void main(String[] args) throws SchedulerException, InterruptedException {
/**
* 1、创建调度器Scheduler
*/
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
/**
* 创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
*/
JobDetail jobDetail = JobBuilder.newJob(DemoJob.class)
.withIdentity("job1", "triggerGroup1")
.build();
/**
* 构建JobDetail实例 每隔1s执行一次
*/
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.startNow()//立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
/**
* 每隔1s执行一次
*/
.withIntervalInSeconds(1)
.repeatForever()).build();//一直执行
/**
* 执行
*/
scheduler.scheduleJob(jobDetail, trigger);
System.out.println("-------scheduler start!---------");
scheduler.start();
/**
* 运行一分钟以后睡眠
*/
TimeUnit.MINUTES.sleep(1);
scheduler.shutdown();
System.out.println("------------scheduler shutdown!--------------");
}
}
运行程序,可以看到程序每隔1s会打印出内容,且在一分钟后结束:
三、Quartz核心详解
下面就程序中出现的几个参数,看一下Quartz框架中的几个重要参数:
- Job和JobDetail
- JobExecutionContext
- JobDataMap
- Trigger、SimpleTrigger、CronTrigger
(1)Job和JobDetail
Job是Quartz中的一个接口,接口下只有execute方法,在这个方法中编写业务逻辑。
接口中的源码:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.quartz;
public interface Job {
void execute(JobExecutionContext var1) throws JobExecutionException;
}
JobDetail用来绑定Job,为Job实例提供许多属性:
- name
- group
- jobClass
- jobDataMap
JobDetail绑定指定的Job,每次Scheduler调度执行一个Job的时候,首先会拿到对应的Job,然后创建该Job实例,再去执行Job中的execute()的内容,任务执行结束后,关联的Job对象实例会被释放,且会被JVM GC清除。
为什么设计成JobDetail + Job,不直接使用Job
JobDetail定义的是任务数据,而真正的执行逻辑是在Job中。
这是因为任务是有可能并发执行,如果Scheduler直接使用Job,
就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,Sheduler每次执行,
都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。
(2)JobExecutionContext
JobExecutionContext中包含了Quartz运行时的环境以及Job本身的详细数据信息。
当Schedule调度执行一个Job的时候,就会将JobExecutionContext传递给该Job的execute()
中,Job就可以通过JobExecutionContext对象获取信息。
主要信息有:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.quartz;
import java.util.Date;
public interface JobExecutionContext {
Scheduler getScheduler();
Trigger getTrigger();
Calendar getCalendar();
boolean isRecovering();
TriggerKey getRecoveringTriggerKey() throws IllegalStateException;
int getRefireCount();
JobDataMap getMergedJobDataMap();
JobDetail getJobDetail();
Job getJobInstance();
Date getFireTime();
Date getScheduledFireTime();
Date getPreviousFireTime();
Date getNextFireTime();
String getFireInstanceId();
Object getResult();
void setResult(Object var1);
long getJobRunTime();
void put(Object var1, Object var2);
Object get(Object var1);
}