Quartz定时器学习个人整理笔记(一)
帮助文档:
Quartz官网
W3Cschool–Quartz官方文档
提示:本文以应用为主,具体原理及概念可以参考如上帮助文档。
文章目录
Quartz的核心概念
- 任务Job
job就是想要实现的任务类,每一个job必须实现org.quartz.job接口,且只需实现接口定义的execute()方法。 - 触发器Trigger
trigger是执行任务的触发器,比如你想每天定时3带你发送一份邮件,trigger将会设置3点进行执行该任务。trigger主要包含SimpleTrigger和CronTrigger。 - 调度器Scheduler
Scheduler是任务的调度器,它会将任务job及触发器trigger整合起来,负责根据trigger设定的时间来执行job。
Quartz的体系结构
Quartz的几个常用API
以下是Quartz编程API几个重要接口,也是Quartz的重要组件:
-
Scheduler:主程序接口,用于与调度程序交互
Scheduler调度程序-任务执行计划表,只有安排进执行计划(通过scheduler.scheduleJob方法安排进执行计划)的任务Job,预先定义的执行时间到了的时候(任务触发trigger),该任务才会执行。
-
JobBuilder:用于声明任务实例,可定义关于该任务的详情如任务名、组名等。其声明的实例将会作为一个实际执行的任务。
-
Job:希望在指定时间能被调度程序执行的任务类,可自定义。
-
JobDetail:用来定义定时任务的实例,JobDetail的实例是由JobBuilder类创建的。
job实例在Quartz中的生命周期:每次调度器执行job时,它在调用execute方法前会创建一个新的job实例,当调用完成后,关联的job对象实例会被释放,释放的实例会被垃圾回收机制回收。
-
JobDataMap:是Java Map接口的一个实现,可以容纳不限量的序列化的数据对象,在jon实例执行时,可使用其中的数据。
-
TriggerBuilder:用于创建触发器trigger实例。
-
Trigger触发器:用于触发Job,表明任务在何时执行。当调度一个Job时,需要实例一个触发器,并根据Job执行的条件调整其属性。
-
JobListener、TriggerListener、SchedulerListener监听器:用于对组件的监听。
触发器介绍
Quartz有一些不同的触发器类型,不过,用的最多的是SimpleTrigger和CronTrigger
(1) jobKey
表示job示例的标识,触发器被触发时,该指定的job实例会被执行。
(2)startTime
表示触发器的时间表,第一次开始被触发的时间,它的数据类型是java.util.Date
1.引入依赖
<dependencies>
<!-- 工具 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz-jobs</artifactId>
<version>2.3.0</version>
</dependency>
<!-- 核心包 -->
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.0</version>
</dependency>
<!-- log4j -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
</dependencies>
<!-- 配置maven的执行环境jdk为1.8 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
</plugins>
</build>
2.SimpleTrigger
可以满足的调度需求:
- 在具体的时间点执行一次。
- 在特定的时间点执行,并以指定的间隔重复执行几次。
SimpleTrigger的属性包括:开始时间,结束时间,重复次数以及重复的间隔。这些属性的含义与您所期望的是一致的,只是关于结束时间有一些地方需要注意。
1.重复次数,可以是0,正整数,以及常量SimpleTrigger.REPEAT_INDEFINITELY。
2.重复的间隔,必须是0,或者长型的正数,表示重复。
注意:如果重复间隔为0,触发将以重复次数并发执行(或者以scheduler可以处理的近似并发数)。
(1)有状态的job和无状态的job
@PersistJobDataAfterExecution注解的使用
有状态的Job可以理解为多次job调用期间可以持有一些状态信息,这些状态信息存储在jobDataMap中,而默认的无状态job每次调用时都会创建一个新的jobDataMap,即不能对信息进行累加。
1. 无状态的job示例(任务类)
public class HelloJob implements Job {
/* public HelloJob(){
System.out.println("欢迎访问!");
}
Job实现类中添加setter方法对应jobDataMap的键值,Quartz框架默认的JobFactory实现类在初始化job
实例对象时会自动地调用这些setter方法. 但是!!!如果遇到同名的key, Trigger中的.usingJobData("message","simple触发器")
会覆盖JobDetail中的.usingJobData("message","打印日志")
*/
private String message;
private Integer count;//jobDetail存储的用于计数
public void setCount(Integer count) {
this.count = count;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 输出当前时间
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//获取任务类信息
String jobName = context.getJobDetail().getJobClass().getName();
System.out.println("任务类名称(带路径):"+context.getJobDetail().getJobClass().getName());
System.out.println("任务类名称:"+context.getJobDetail().getJobClass().getSimpleName());
//获取JobDetail任务实例的内容
JobKey jobkey=context.getJobDetail().getKey();
// System.out.println("工作任务名称:"+jobkey.getName()+" ,工作任务组:"+jobkey.getGroup());
//获取trigger的内容
TriggerKey triggerKey = context.getTrigger().getKey();
System.out.println("触发器名称:"+triggerKey.getName()+" ,触发器组:"+triggerKey.getGroup());
/* //在jobDetail中获取jobDataMap存储的值
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
String jobDataMessage = jobDataMap.getString("message");
System.out.println("任务数据的参数值"+jobDataMessage);
//获取触发器中jobDataMap存储的值
String triggerMessage = context.getTrigger().getJobDataMap().getString("message");
System.out.println("trigger数据的参数值"+triggerMessage); */
/*从set方法中获取message 注意!!!如果遇到同名的key, Trigger中的.usingJobData("message","simple触发器")
会覆盖JobDetail中的.usingJobData("message","打印日志") */
System.out.println("从setter方法获取到的message为:"+message);
//获取任务的执行时间
System.out.println("当前任务的执行时间:"+sdf.format(context.getFireTime()));
System.out.println("下一次任务的执行时间:"+sdf.format(context.getNextFireTime()));
//累加count,将count存放到jobDataMap中 无状态的job信息
++count;
context.getJobDetail().getJobDataMap().put("count", count);
System.out.println("count:"+count);
}
}
任务调度类
public class HelloSchedulerDemo {
public static void main(String args[]) throws SchedulerException, ParseException {
// 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
JobDetail job1 = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1") // 参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
.usingJobData("message","打印日志") //传map类型的值
.usingJobData("count",0)
.build();
// System.out.println("任务名称"+job1.getKey().getName());
// System.out.println("组名称"+job1.getKey().getGroup());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
SimpleTrigger trigger2 = TriggerBuilder.newTrigger().withIdentity("trigger2", "group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
.endAt(sdf.parse("2099-1-18 15:29:00"))
.usingJobData("message","simple触发器") //传递参数,名称message
.build();
// 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(job1, trigger2);
// 5.启动调度
scheduler.start();
}
}
执行结果: count并没有累加1
2. 有状态的job示例(在任务类上加个注解)
@PersistJobDataAfterExecution //多次调用Job期间可以持有一些状态信息,即可以实现count的累加
public class HelloJob implements Job {
// 方法内部代码同上...
}
执行结果: count累加1
(2)入门案例
SimpleTrigger简单案例
1.任务类(HelloJob):
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext arg0) throws JobExecutionException {
// 输出当前时间
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//工作内容
System.out.println("正在执行任务...执行时间"+date.toString()+"格式化后:"+sdf.format(date));
}
}
2.创建任务调度类(HelloSchedulerDemo):
public class HelloSchedulerDemo {
public static void main(String args[]) throws SchedulerException{
//1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1") //参数1:任务的名称(唯一实例); 参数2:任务组的名称,多个任务可以在同一个任务组中
.build();
System.out.println("任务名称"+job1.getKey().getName());
//如果没有指定组名,默认是DEFAULT
System.out.println("组名称"+job1.getKey().getGroup());
//3.生成触发器实例(Trigger)
SimpleTrigger trigger1 = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "One")//参数1:触发器的名称(唯一实例); 参数2:触发器组的名称.不必与任务组名称一致!
.startNow() //马上启动触发器
.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(3))//使用SimpleScheduleBuilder,每5秒钟执行一次
.build();
//4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(jobDetail,trigger1 );
//5.启动调度
scheduler.start();
}
}
2.1创建任务调度类(HelloSchedulerDemo), 实现结束时间内每5秒执行一次:
public class HelloSchedulerDemo {
public static void main(String args[]) throws SchedulerException, ParseException{
//1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
JobDetail job1 = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1") //参数1:任务的名称(唯一实例); 参数2:任务组的名称,多个任务可以在同一个任务组中
.build();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
SimpleTrigger trigger2=TriggerBuilder.newTrigger().withIdentity("trigger2","group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5)
.repeatForever())
.endAt(sdf.parse("2021-1-18 14:29:00"))
.build();
//4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(job1,trigger2 );
//5.启动调度
scheduler.start();
}
}
3.执行结果(部分展示):
3.1 执行结果(部分展示):
2.2
public class HelloSchedulerDemo {
public static void main(String args[]) throws SchedulerException, ParseException {
// 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
// 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
JobDetail job1 = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1") // 参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
.build();
System.out.println("任务名称"+job1.getKey().getName());
System.out.println("组名称"+job1.getKey().getGroup());
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
SimpleTrigger trigger2 = TriggerBuilder.newTrigger().withIdentity("trigger2", "group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
.endAt(sdf.parse("2099-1-18 15:29:00")).build();
// 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(job1, trigger2);
// 5.启动调度
scheduler.start();
}
}
public class HelloJob implements Job {
public HelloJob(){
System.out.println("欢迎访问!");
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 输出当前时间
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//获取JobDetail的内容
JobKey jobkey=context.getJobDetail().getKey();
String jobName = context.getJobDetail().getJobClass().getName();
System.out.println("工作任务名称:"+jobkey.getName()+" ,工作任务组:"+jobkey.getGroup());
System.out.println("任务类名称(带路径):"+context.getJobDetail().getJobClass().getName());
System.out.println("任务类名称:"+context.getJobDetail().getJobClass().getSimpleName());
//工作内容
System.out.println("正在执行任务...执行时间"+date.toString()+"格式化后:"+sdf.format(date));
}
}
3.2执行结果(部分展示):
2.3 指定开始时间和结束时间内,循环执行,时间间隔自定义(任务类)
public class HelloJob implements Job {
private String message;
private Integer count;//jobDetail存储的用于计数
public void setCount(Integer count) {
this.count = count;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 输出当前时间
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//工作内容
System.out.println("正在执行任务...时间是:"+sdf.format(date));
//获取jobKey、startTime、endTime
Trigger trigger = context.getTrigger();
System.out.println("jobKey名称:"+trigger.getJobKey().getName()+" ,jobGroup名称:"+trigger.getJobKey().getGroup());
System.out.println("任务的开始时间:"+sdf.format(trigger.getStartTime())+" ;结束时间:"+sdf.format(trigger.getEndTime()));
}
}
创建任务调度类:
public class HelloSchedulerDemo {
public static void main(String args[]) throws SchedulerException, ParseException {
// 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//设置任务的开始/结束时间
Date startDate = new Date();
Date endDate = new Date();
//设置任务的开始时间推迟3秒
startDate.setTime(startDate.getTime()+3000);
//任务的结束时间推迟10秒(即10秒后停止)
endDate.setTime(endDate.getTime()+10000);
// 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
JobDetail job1 = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1") // 参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
.usingJobData("message","打印日志") //传map类型的值
.usingJobData("count",0)
.build();
// 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
Trigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(1)) //循环执行,1秒执行一次
.startAt(startDate) //设置任务的开始时间
.endAt(endDate)
.build();
// 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(job1, trigger2);
// 5.启动调度
scheduler.start();
}
}
3.3执行结果
2.4 指定开始时间,循环执行时间间隔自定义,执行次数自定义(任务类同上) .withRepeatCount(3),参数为0时执行1次
创建任务调度类:
public class HelloSchedulerDemo {
public static void main(String args[]) throws SchedulerException, ParseException {
// 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//设置任务的开始/结束时间
Date startDate = new Date();
//设置任务的开始时间推迟3秒
startDate.setTime(startDate.getTime()+3000);
// 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
JobDetail job1 = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1") // 参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
.build();
// 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
Trigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(1)
.withRepeatCount(3)) //循环执行,1秒执行一次,只会执行4次
//.withRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY)) //循环执行,-1意思一直循环,跟不写没区别
.startAt(startDate) //设置任务的开始时间
.build();
// 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(job1, trigger2);
// 5.启动调度
scheduler.start();
}
}
注意:当SimpleTrigger设置的任务结束时间内,任务执行次数未全部执行时,实际任务执行次数以结束时间为准 !!!
3.CronTrigger触发器
应用场景: 特定日期(如: 2020年12月2日) / 特定周期(每个工作日的9:30 ; 每个周一,周三,周五的上午9:00至10:00之间每隔5分钟执行一次) 等。也可像SimpleTrigger一样,设置startDate和endDate。
(1)Cron Expressions——Cron 表达式
Cron表达式用来配置CronTrigger实例。Cron表达式是一个由7个子表达式组成的字符串。每个子表达式都描述了一个单独的日程细节。这些子表达式用空格分隔,分别表示:
- Seconds 秒
- Minutes 分钟
- Hours 小时
- Day-of-Month 月中的天
- Month 月
- Day-of-Week 周几
- Year 年(选填)
(2)示例代码
案例1: 实现1月22日开始执行,从0秒开始,每5秒执行一次
1.任务类
public class HelloJobCronTrigger implements Job {
private String message;
private Integer count;//jobDetail存储的用于计数
public void setCount(Integer count) {
this.count = count;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 输出当前时间
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//工作内容
System.out.println("正在执行任务...时间是:"+sdf.format(date));
//获取jobKey、startTime、endTime
Trigger trigger = context.getTrigger();
System.out.println("jobKey名称:"+trigger.getJobKey().getName()+" ,jobGroup名称:"+trigger.getJobKey().getGroup());
System.out.println("任务的开始时间:"+sdf.format(trigger.getStartTime())+" ;结束时间:"+sdf.format(trigger.getEndTime()));
}
}
2.任务调度类
public class HelloSchedulerDemo {
public static void main(String args[]) throws SchedulerException, ParseException {
// 1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory();)
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//设置任务的开始/结束时间
Date startDate = new Date();
Date endDate = new Date();
//设置任务的开始时间推迟3秒
startDate.setTime(startDate.getTime()+3000);
endDate.setTime(startDate.getTime()+10000);
// 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
JobDetail job1 = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1") // 参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
.build();
// 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
Trigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.startAt(startDate) //设置任务的开始时间
.endAt(endDate)
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * 22 1 ?")) //1月22日开始执行,从0秒开始,每5秒执行一次
.build();
// 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
scheduler.scheduleJob(job1, trigger2);
// 5.启动调度
scheduler.start();
}
}
案例2
1.任务类
public class HelloJobCronTrigger implements Job {
private String message;
private Integer count;//jobDetail存储的用于计数
public void setCount(Integer count) {
this.count = count;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 输出当前时间
Date date=new Date();
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//工作内容
System.out.println("正在执行任务...时间是:"+sdf.format(date));
//获取jobKey、startTime、endTime
Trigger trigger = context.getTrigger();
System.out.println("jobKey名称:"+trigger.getJobKey().getName()+" ,jobGroup名称:"+trigger.getJobKey().getGroup());
System.out.println("任务的开始时间:"+sdf.format(trigger.getStartTime())+" ;结束时间:"+sdf.format(trigger.getEndTime()));
}
}
2.任务调度类
public class HelloSchedulerCronTriggerDemo {
public static void main(String args[]) throws SchedulerException, ParseException, InterruptedException {
/*1.生成调度器(Scheduler)实例,从工厂中获取调度的实例(默认:实例化new StdSchedulerFactory() )
*
*法一: Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();*/
StdSchedulerFactory schedulerFactory=new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
//设置任务的开始/结束时间
Date startDate = new Date();
Date endDate = new Date();
//设置任务的开始时间推迟3秒
startDate.setTime(startDate.getTime()+3000);
endDate.setTime(startDate.getTime()+10000);
// 2.生成任务实例(JobDetail),加载任务Job类,与HelloJob完成绑定,要求:helloJob必须实现Job接口
JobDetail job1 = JobBuilder.newJob(HelloJob.class)
.withIdentity("job1", "group1") // 参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
.build();
// 3.生成触发器实例(Trigger),立即触发,每5秒钟执行一次,直到2021-1-18 14:29:00
Trigger trigger2 = TriggerBuilder.newTrigger()
.withIdentity("trigger2", "group1")
.startAt(startDate) //设置任务的开始时间
.endAt(endDate)
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * 22 1 ?")) //1月22日开始执行,从0秒开始,每5秒执行一次
.build();
// 4.让调度器关联任务和触发器,保证按照触发器定义的条件执行任务
Date jobDate = scheduler.scheduleJob(job1, trigger2);
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("调度器的开始时间是:"+sdf.format(jobDate));
// 5.启动调度
scheduler.start();
//挂起调度
scheduler.standby();
/*关闭调度,关闭后不能再开启; shutdown(true):表示等待所有正在执行的job执行完毕后,再关闭Scheduler;
shutdown(false):表示直接关闭Scheduler
scheduler.shutdown();*/
//scheduler执行5秒后自动开启
Thread.sleep(5000L);
//重新启动任务
scheduler.start();
}
}
(3)DirectSchedulerFactory(了解)
DirectSchedulerFactory是对SchedulerFactory的直接实现,通过它可以直接构建Scheduler、threadpool等。
DirectSchedulerFactory instance = DirectSchedulerFactory.getInstance();
Scheduler scheduler = instance.getScheduler();
4. Quartz.properties文件
(1)参考代码
#设置默认调度程序的名称,如正在使用群集功能,则必须对群集中“逻辑上”相同的调度程序的每个实例使用相同的名称,重新赋值该值。
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
#如果您希望Quartz Scheduler通过RMI作为服务器导出本身,则为true。
org.quartz.scheduler.rmi.export = false
#如果要连接(使用)远程服务的调度程序,则为true。还必须指定RMI注册表进程的主机和端口 - 通常是“localhost”端口1099
org.quartz.scheduler.rmi.proxy = false
#org.quartz.scheduler.rmi.registryHost
#org.quartz.scheduler.rmi.registryPort
#设置这项为true使我们在调用job的execute()之前能够开始一个UserTransaction。
org.quartz.scheduler.wrapJobExecutionInUserTransaction = false
#指定的线程池
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
#线程数量
org.quartz.threadPool.threadCount = 10
#优先级
org.quartz.threadPool.threadPriority = 5
#自创建父线程
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread = true
#作业最大延迟时间毫秒
org.quartz.jobStore.misfireThreshold = 60000
#数据保存方式为持久化
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore
(2)组成部分
-
调度器属性
org.quartz.scheduler.instanceName属性用来区分特定的调度器实例,可以按照功能用途来给调度器取名。
org.quartz.scheduler.instanceId属性和前者一样,也允许任何字符串,但这个值必须在所有调度器实例中是唯一的,尤其是在一个集群环境中,作为集群的唯一key。如果你想让Quartz帮你生成这个值,可以设置为Auto。 -
线程池属性
threadCount:处理job的线程个数,至少是1,但最多不要超过100为宜,在多数机器上设置该值超过100的话会显得相当不实用,特别是在job执行时间较长的情况下
threadPriority:线程的优先级,最高为10,最低为1,默认为5,优先级越高越先执行。
org.quartz.threadPool.class:实现了org.quartz.spi.ThreadPool接口,Quartz自带的线程池实现类是org.quartz.smpl.SimpleThreadPool。
-
作业存储设置
描述了调度器的生命周期中,job和trigger信息时如何被存储的。
-
插件配置
满足特定需求用到的Quartz插件的配置。
(3)代码配置Quartz属性(了解)
除了使用quartz.properties文件对定时器进行配置之外,还通过代码配置quartz的属性。不过由于修改远不如配置文件方便,故了解即可。示例代码如下:
public class QuartzProperties {
public static void main(String[] args) {
//创建工厂实例
StdSchedulerFactory stdSchedulerFactory = new StdSchedulerFactory();
//创建配置工厂的属性的对象
Properties prop=new Properties();
//定义线程池
// prop.put("org.quartz.threadPool.class", "org.quartz.simpl.SimpleThreadPool");
prop.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool");
prop.put("org.quartz.threadPool.threadCount", "5"); //必须是string型数据
try {
//加载配置工厂定义的属性
stdSchedulerFactory.initialize(prop);
Scheduler scheduler = stdSchedulerFactory.getScheduler();
//开启实例
scheduler.start();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
}
5. Quartz监听器
(1)概念
Quartz的监听器用于当任务调度中你所关注事件发生时,能够及时获取这一事件的通知。类似于任务执行过程中的邮件、短信类提醒。Quartz监听器主要有JobListener(任务监听器)、TriggerListener(触发器监听器)、 SchedulerListener(调度器监听器) 三种。
核心思想:要创建一个listener,只需创建一个实现org.quartz.TriggerListener和/或org.quartz.JobListener接口的对象。然后,listener在运行时会向调度程序注册,并且必须给出一个名称(或者,他们必须通过他们的getName()方法来宣传自己的名字)。
listener与调度程序的ListenerManager一起注册,并配有描述listener希望接收事件的job/触发器的Matcher。
监听器的两个类型:全局监听器和非全局监听器,二者的区别:
- 全局监听器能够接收到所有的job/Trigger的事件通知
- 非全局监听器只能接受到在其注册的Job或Trigger的时间,没有注册的Job和Trigger则不会进行监听。
(2)JobListener
任务调度过程中,与任务Job相关的事件包括:job即将执行的通知,以及job完成执行时的通知。
1)getName() : 获取该JobListener的名称
2)jobToBeExecuted() : Scheduler在JobDetail将要被执行时调用这个方法
3)jobExecutionVetoed() : Scheduler在JobDetail即将被执行,但又被TriggerListener否决时会调用该方法
4)jobWasExecuted() : Scheduler在JobDetail被执行之后调用这个方法
1.全局JobListener
任务调度类:
//创建并注册一个全局的job Listener
JobDetail job1 = JobBuilder.newJob(HelloJobListener.class)
.withIdentity("job1", "group1") // 参数1:任务的名称(唯一实例);参数2:任务组的名称,多个任务可以在同一个任务组中
.build();
.........逻辑代码
scheduler.getListenerManager().addJobListener(new MyJobListener(),EverythingMatcher.allJobs());
任务监听器类JobListener:
public class HelloJobListener implements JobListener {
private String message;
private Integer count;// jobDetail存储的用于计数
public void setCount(Integer count) {
this.count = count;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
// 输出当前时间
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 工作内容
System.out.println("正在执行任务...时间是:" + sdf.format(date));
}
/*
* 获取该JobListener的名称
*/
@Override
public String getName() {
String simpleName = this.getClass().getSimpleName();
System.out.println("监听器的名称:" + simpleName);
return simpleName;
}
/*
* Scheduler在JobDetail将要被执行时调用这个方法
*/
@Override
public void jobToBeExecuted(JobExecutionContext context) {
String jname = context.getJobDetail().getKey().getName();
System.out.println("job的名称是:" + jname + " Scheduler在JobDetail将要被执行时调用的方法");
}
/*
* Scheduler在JobDetail即将被执行,但又被TriggerListener否决时会调用该方法
*/
@Override
public void jobExecutionVetoed(JobExecutionContext context) {
String jname = context.getJobDetail().getKey().getName();
System.out.println("job的名称是:" + jname + " Scheduler在JobDetail即将被执行,但又被TriggerListener否决时会调用该方法");
}
/*
* Scheduler在JobDetail被执行之后调用这个方法
*/
@Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
String jname = context.getJobDetail().getKey().getName();
System.out.println("job的名称是:" + jname + " Scheduler在JobDetail被执行之后调用这个方法");
}
}
执行结果:
2.局部JobListener
任务调度类:
//添加对特定job感兴趣的JobListener
scheduler.getListenerManager().addJobListener(myJobListener,KeyMatcher.jobKeyEquals(new JobKey("myJobName","myJobGroup")));
//或者
scheduler.getListenerManager().addJobListener(myJobListener, jobKeyEquals(jobKey("myJobName", "myJobGroup")));
// 添加对特定组的所有job感兴趣的JobListener
scheduler.getListenerManager().addJobListener(myJobListener, jobGroupEquals("myJobGroup"));
//添加对两个特定组的所有job感兴趣的JobListener
scheduler.getListenerManager().addJobListener(myJobListener, or(jobGroupEquals("myJobGroup"), jobGroupEquals("yourGroup")));
任务监听器类JobListener同上一个例子
注册TriggerListeners的工作原理相同。
Quartz的大多数用户并不使用Listeners,但是当应用程序需求创建需要事件通知时不需要Job本身就必须明确地通知应用程序,这些用户就很方便。
(3)SchedulerListeners
SchedulerListeners非常类似于TriggerListeners和JobListeners,除了它们在Scheduler本身中接收到事件的通知 - 不一定与特定触发器(trigger)或job相关的事件。
与计划程序相关的事件包括:添加job/触发器,删除job/触发器,调度程序中的严重错误,关闭调度程序的通知等。
1)** jobScheduled(Trigger trigger)** : 用于部署jobDetail时调用
2)jobUnscheduled(String triggerName, String triggerGroup) : 用于卸载jobDetail时调用
3)triggerFinalized(Trigger trigger) : 当一个Trigger来到了再也不会触发的状态时调用这个方法。除非这个job已设置成了持久性,否则它就会从Scheduler中移除。
4)triggersPaused(String triggerName, String triggerGroup) : Scheduler调用这个方法是发生在一个Trigger或Trigger组被暂停时。假如是Trigger组的话,triggerName参数将为null。
5)triggersResumed(String triggerName, String triggerGroup) : Scheduler调用这个方法是发生在一个Trigger或Trigger组从暂停中恢复时。如果是Trigger组的话,triggerName参数将为null。
6)jobsPaused(String jobName, String jobGroup) : 当一个或一组jobDetail暂停时调用这个方法。
7)jobsResumed(String jobName, String jobGroup) : 当一个或一组job从暂停上恢复时调用这个方法。如果是Job组的话,jobName参数将为null。
8)schedulerError(String msg, SchedulerException cause) : 在Scheduler的正常运行期间发生严重错误时调用这个方法。
9)schedulerStarted() : 当Scheduler开启时,调用该方法。
10)schedulerInStandbyMode() : 当Scheduler处于StandBy模式时,调用该方法。
11)schedulerShutdown() : 当Scheduler停止时,调用该方法。
12)schedulingDataCleared() : 当Scheduler中的数据被清除时,调用该方法。