- Maven依赖
- 三大组件:Job、Trigger、Scheduler
- 第一个quartz程序
- 调度器Scheduler
- 任务 Job和JobDetail
- 触发器 Trigger
- 完整demo代码
- quartz.properties 配置文件
- Default Properties file for use by StdSchedulerFactory
- to create a Quartz Scheduler Instance, if a different
- properties file is not explicitly specified.
- 区分调度器实例 所有调度器实例中唯一 只有一个的时候可以不配置
- 区分调度器实例 名
- 基于内存存储job 也可以配置成基于数据库
Maven依赖
<dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.3.2</version></dependency>
三大组件:Job、Trigger、Scheduler
- Job——任务
- Trigger——触发器
- Scheduler——调度器
任务和触发器进行绑定,然后由调度器调用触发器进行执行任务
关系大概如下
Scheduler与Trigger一对多
Trigger与JobDetail多对一
JobDetail与Job多对一
第一个quartz程序
public static void main(String[] args) {
try {
//调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
System.out.println(scheduler.getSchedulerName());
System.out.println("线程个数" + scheduler.getMetaData().getThreadPoolSize());
scheduler.shutdown();
} catch (SchedulerException e) {
e.printStackTrace();
}
}
scheduler启动任务,scheduler停止任务,没有用到job trigger
注意静态导入
import static org.quartz.CronScheduleBuilder.cronSchedule;
import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
import static org.quartz.TriggerBuilder.newTrigger;
使用这些类的方法时不需要在方法名前写类和点,下面的代码都用到了
调度器Scheduler
使用工厂模式创建对象
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler的一些api
scheduler.start();
//添加jobdetail 将job保存在内存中
scheduler.addJob(job, false);
//使用调度器调度 这样绑定就不需要上面那一行
scheduler.scheduleJob(job, trigger1);
scheduler.scheduleJob(trigger2);
scheduler.shutdown();
//根据jobdetail对象进行一些操作
//删除job
scheduler.deleteJob(detail.getKey());
//继续Job
scheduler.resumeJob(detail.getKey());
//暂停Job
scheduler.pauseJob(detail.getKey());
//取消job
scheduler.unscheduleJob(trigger.getKey());
//结束任务
scheduler.shutdown();
任务 Job和JobDetail
Job接口实现类
要继承Job接口
在execute方法里执行具体的任务方法
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Trigger trigger = context.getTrigger();
JobDataMap jobDataMap = trigger.getJobDataMap();
System.out.println(trigger.getKey() + " job... "
+ DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
.format(LocalDateTime.now()));
}
}
JobExecutionContext 接口对象可以获取一些信息,比如获取trigger获取存放的数据JobDataMap
具体方法可以点进去看
JobDetail
新建任务将上面实现类绑定,提供给trigger绑定
//创建任务名和任务组
JobKey jobKey = new JobKey("job1", "group1");
//新建任务
JobDetail job = newJob(HelloJob.class)
.withIdentity(jobKey)
//没有trigger关联的时候也存起来
.storeDurably()
.build();
或者这样写
//新建任务
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
//没有trigger关联的时候也存起来
.storeDurably()
.build();
可以在创建JobDetail对象是添加JobDataMap信息
//新建任务
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
//JobDataMap 键值对
.usingJobData("key", "value")
//没有trigger关联的时候也存起来
.storeDurably()
.build();
JobDataMap
usingJobData有很多方法重载
可以在新建JobDetail时新增key value,可以无限链式调用
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData("key", "123123123")
//没有trigger关联的时候也存起来
.storeDurably()
.build();
也可以在Trigger里设置
Trigger trigger1 = newTrigger()
.withIdentity("trigger1", "group1")
.usingJobData("key","trigger trigger trigger")
.startNow()
.withSchedule(
simpleSchedule()
.withIntervalInSeconds(1)
//执行多少次
.withRepeatCount(20)
.repeatForever()
).build();
在JobExecutionContext里可以获得
public class HelloJob implements Job {
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
Trigger trigger = context.getTrigger();
System.out.println("================start=================");
JobDataMap detailMap = context.getJobDetail().getJobDataMap();
String key = detailMap.getString("key");
JobDataMap triggerJobDataMap = trigger.getJobDataMap();
String triggerKey = triggerJobDataMap.getString("key");
System.out.println(key);
System.out.println(triggerKey);
}
}
在Job接口实现类中
可以进行get set方法设置进行获取
@Getter
@Setter
@Slf4j
public class HelloJob implements Job {
private String key;
private String name;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("================start=================");
System.out.println(key);
System.out.println(name);
}
}
不管是JobDetail还是Trigger都能获取,通过这俩key同名就会冲突,随机获取一个对应
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData("key", "123123123")
//没有trigger关联的时候也存起来
.storeDurably()
.build();
//触发器
Trigger trigger1 = newTrigger()
.withIdentity("trigger1", "group1")
.usingJobData("name", "trigger trigger trigger")
.startNow()
.withSchedule(
simpleSchedule()
.withIntervalInSeconds(1)
//执行多少次
.withRepeatCount(20)
.repeatForever()
).build();
JobDataMap更新
在Job接口实现类中加注解 @PersistJobDataAfterExecution
然后给对应的值put,不加此注解直接put不会生效
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
@Getter
@Setter
@Slf4j
public class HelloJob implements Job {
private String key;
private String name;
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
System.out.println("================start=================");
System.out.println(key);
System.out.println(name);
context.getJobDetail().getJobDataMap().put("key", key + 1);
}
}
防止定时任务并发执行

在Job接口实现类上加个注解就行了
@DisallowConcurrentExecution
public class HelloJob implements Job{
//...
}
触发器 Trigger
和JobDetail任务进行绑定,由Scheduler进行调度,一个Trigger只能和一个JobDetail绑定,多个Trigger执行同一JobDeTail会保存
配置任务执行的策略,可以配置cron表达式
//触发器
Trigger trigger1 = newTrigger()
.withIdentity("trigger1", "group1")
//立即执行
.startNow()
.endAt(
//什么时候结束
Date.from(LocalDateTime.now().plusSeconds(5).atZone(ZoneId.systemDefault()).toInstant())
)
.withSchedule(
// simple触发器
simpleSchedule()
//执行周期 隔一秒
.withIntervalInSeconds(1)
//执行多少次
//.withRepeatCount(1)
.repeatForever()
).build();
这样没有指定job的名和组就需要scheduler来绑定
scheduler.scheduleJob(job, trigger1);
Trigger trigger2 = newTrigger()
.withIdentity("trigger2", "group1")
//指定执行开始时间
.startAt(
Date.from(LocalDateTime.now().plusSeconds(5).atZone(ZoneId.systemDefault()).toInstant())
)
//指定job的名字和组
.forJob("job1", "group1")
.withSchedule(
//cron触发器
// cron表达式
cronSchedule("* * * * * ?")
).build();
指定了job的名和组需要scheduler先保存job,然后在通过trigger执行任务
//添加job
scheduler.addJob(job, false);
scheduler.scheduleJob(trigger2);
优先级
当线程数小于Trigger数时且同一时间执行任务时才有效
数字越大优先级越高
Trigger trigger1 = newTrigger()
.withIdentity("trigger1", "group1")
.usingJobData("name", "trigger trigger trigger")
//设置优先级的值 默认是5
.withPriority(5)
.startNow()
.withSchedule(
simpleSchedule()
.withIntervalInSeconds(1)
//执行多少次
.withRepeatCount(20)
.repeatForever()
).build();
Misfire机制
错过机制
触发器不执行,到了时间本应该触发,结果没有
Simple触发器和Cron触发器都有自己的应对策略
产生原因
- 线程数少于Trigger数,只能有一部分Trigger执行
- 工作时长大于定时任务的时间间隔
完整demo代码
public static void main(String[] args) {
try {
//调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
JobKey jobKey = new JobKey("job1", "group1");
//新建任务
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.usingJobData("key", "value")
//没有trigger关联的时候也存起来
.storeDurably()
.build();
//触发器
Trigger trigger1 = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.endAt(
Date.from(LocalDateTime.now().plusSeconds(5).atZone(ZoneId.systemDefault()).toInstant())
)
.withSchedule(
simpleSchedule()
.withIntervalInSeconds(1)
//执行多少次
//.withRepeatCount(1)
.repeatForever()
).build();
Trigger trigger2 = newTrigger()
.withIdentity("trigger2", "group1")
.startAt(
Date.from(LocalDateTime.now().plusSeconds(5).atZone(ZoneId.systemDefault()).toInstant())
)
//指定job
.forJob("job1", "group1")
.withSchedule(
// cron表达式
cronSchedule("* * * * * ?")
).build();
//添加job
scheduler.addJob(job, false);
//使用调度器调度
scheduler.scheduleJob(job, trigger1);
scheduler.scheduleJob(trigger2);
//主线程睡20秒 让定时任务执行20秒
Thread.sleep(TimeUnit.SECONDS.toMillis(20));
scheduler.shutdown();
} catch (SchedulerException | InterruptedException e) {
e.printStackTrace();
}
}
quartz.properties 配置文件
如果工程没有 quartz就会去依赖的jar里去找
文件名就是quartz.properties
组成部分
- 调度器属性
- 线程池属性
- 作业存储位置
- 插件配置
```xml
Default Properties file for use by StdSchedulerFactory
to create a Quartz Scheduler Instance, if a different
properties file is not explicitly specified.
#
区分调度器实例 所有调度器实例中唯一 只有一个的时候可以不配置
org.quartz.scheduler.instanceId: 111
区分调度器实例 名
org.quartz.scheduler.instanceName: DefaultQuartzScheduler org.quartz.scheduler.rmi.export: false org.quartz.scheduler.rmi.proxy: false 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
基于内存存储job 也可以配置成基于数据库
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
复杂一点的
```java
# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
# ===========================================================================
# Configure Main Scheduler Properties 调度器属性
# ===========================================================================
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.instanceid:AUTO
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
# ===========================================================================
# Configure ThreadPool 线程池属性
# ===========================================================================
#线程池的实现类(一般使用SimpleThreadPool即可满足几乎所有用户的需求)
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
#指定线程数,至少为1(无默认值)(一般设置为1-100直接的整数合适)
org.quartz.threadPool.threadCount: 10
#设置线程的优先级(最大为java.lang.Thread.MAX_PRIORITY 10,最小为Thread.MIN_PRIORITY 1,默认为5)
org.quartz.threadPool.threadPriority: 5
#设置SimpleThreadPool的一些属性
#设置是否为守护线程
#org.quartz.threadpool.makethreadsdaemons = false
#org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
#org.quartz.threadpool.threadsinheritgroupofinitializingthread=false
#线程前缀默认值是:[Scheduler Name]_Worker
#org.quartz.threadpool.threadnameprefix=swhJobThead;
# 配置全局监听(TriggerListener,JobListener) 则应用程序可以接收和执行 预定的事件通知
# ===========================================================================
# Configuring a Global TriggerListener 配置全局的Trigger监听器
# MyTriggerListenerClass 类必须有一个无参数的构造函数,和 属性的set方法,目前2.2.x只支持原始数据类型的值(包括字符串)
# ===========================================================================
#org.quartz.triggerListener.NAME.class = com.swh.MyTriggerListenerClass
#org.quartz.triggerListener.NAME.propName = propValue
#org.quartz.triggerListener.NAME.prop2Name = prop2Value
# ===========================================================================
# Configuring a Global JobListener 配置全局的Job监听器
# MyJobListenerClass 类必须有一个无参数的构造函数,和 属性的set方法,目前2.2.x只支持原始数据类型的值(包括字符串)
# ===========================================================================
#org.quartz.jobListener.NAME.class = com.swh.MyJobListenerClass
#org.quartz.jobListener.NAME.propName = propValue
#org.quartz.jobListener.NAME.prop2Name = prop2Value
# ===========================================================================
# Configure JobStore 存储调度信息(工作,触发器和日历等)
# ===========================================================================
# 信息保存时间 默认值60秒
org.quartz.jobStore.misfireThreshold: 60000
#保存job和Trigger的状态信息到内存中的类
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore
# ===========================================================================
# Configure SchedulerPlugins 插件属性 配置
# ===========================================================================
# 自定义插件
#org.quartz.plugin.NAME.class = com.swh.MyPluginClass
#org.quartz.plugin.NAME.propName = propValue
#org.quartz.plugin.NAME.prop2Name = prop2Value
#配置trigger执行历史日志(可以看到类的文档和参数列表)
org.quartz.plugin.triggHistory.class = org.quartz.plugins.history.LoggingTriggerHistoryPlugin
org.quartz.plugin.triggHistory.triggerFiredMessage = Trigger {1}.{0} fired job {6}.{5} at: {4, date, HH:mm:ss MM/dd/yyyy}
org.quartz.plugin.triggHistory.triggerCompleteMessage = Trigger {1}.{0} completed firing job {6}.{5} at {4, date, HH:mm:ss MM/dd/yyyy} with resulting trigger instruction code: {9}
#配置job调度插件 quartz_jobs(jobs and triggers内容)的XML文档
#加载 Job 和 Trigger 信息的类 (1.8之前用:org.quartz.plugins.xml.JobInitializationPlugin)
org.quartz.plugin.jobInitializer.class = org.quartz.plugins.xml.XMLSchedulingDataProcessorPlugin
#指定存放调度器(Job 和 Trigger)信息的xml文件,默认是classpath下quartz_jobs.xml
org.quartz.plugin.jobInitializer.fileNames = my_quartz_job2.xml
#org.quartz.plugin.jobInitializer.overWriteExistingJobs = false
org.quartz.plugin.jobInitializer.failOnFileNotFound = true
#自动扫描任务单并发现改动的时间间隔,单位为秒
org.quartz.plugin.jobInitializer.scanInterval = 10
#覆盖任务调度器中同名的jobDetail,避免只修改了CronExpression所造成的不能重新生效情况
org.quartz.plugin.jobInitializer.wrapInUserTransaction = false
# ===========================================================================
# Sample configuration of ShutdownHookPlugin ShutdownHookPlugin插件的配置样例
# ===========================================================================
#org.quartz.plugin.shutdownhook.class = \org.quartz.plugins.management.ShutdownHookPlugin
#org.quartz.plugin.shutdownhook.cleanShutdown = true
#
# Configure RMI Settings 远程服务调用配置
#
#如果你想quartz-scheduler出口本身通过RMI作为服务器,然后设置“出口”标志true(默认值为false)。
#org.quartz.scheduler.rmi.export = false
#主机上rmi注册表(默认值localhost)
#org.quartz.scheduler.rmi.registryhost = localhost
#注册监听端口号(默认值1099)
#org.quartz.scheduler.rmi.registryport = 1099
#创建rmi注册,false/never:如果你已经有一个在运行或不想进行创建注册
# true/as_needed:第一次尝试使用现有的注册,然后再回来进行创建
# always:先进行创建一个注册,然后再使用回来使用注册
#org.quartz.scheduler.rmi.createregistry = never
#Quartz Scheduler服务端端口,默认是随机分配RMI注册表
#org.quartz.scheduler.rmi.serverport = 1098
#true:链接远程服务调度(客户端),这个也要指定registryhost和registryport,默认为false
# 如果export和proxy同时指定为true,则export的设置将被忽略
#org.quartz.scheduler.rmi.proxy = false
