quartz学习笔记
Posted 若曦`
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了quartz学习笔记相关的知识,希望对你有一定的参考价值。
1. quartz组件
(1) Job
编写Job类 (任务类),实现Job接口,重写exeute方法,此方法就是要执行的任务,类似于Runable
public class MyJob implements Job
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException
System.out.println("任务正在执行 - "+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
(2) JobDetail
JobDetail是对Job的封装,可以设置许多属性,还包括一个比较重要的JobDataMap,用来存储Job实例的状态信息
调度器需要借助JobDetail对象来添加Job实例
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
.withIdentity("任务1","组1")
.build();
(3) trigger
触发器用来告诉调度程序任务什么时候触发,框架提供了5种触发器类型,但两个最常用的SimpleTrigger和CronTrigger
- SimpleTrigger:执行N次,重复N次
- CronTrigger:几秒 几分 几时 哪日 哪月 哪周 哪年,执行
//触发器
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("触发器1","组1")
//触发策略 可以设置是间隔执行,还是到某个时间执行
//此处为间隔1秒执行一次,一直执行
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
.build();
trigger的优先级
-
优先级数字越大触发器优先级越高,在触发器并发执行情况下,优先级越高的触发器执行越靠前
-
若触发器优先级未设置,那么优先级最低
Trigger trigger = TriggerBuilder.newTrigger()
//设置trigger的优先级
.withPriority(10)
.withIdentity("触发器1","组1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
.build();
(4) Scheduler
Scheduler称之为调度器,用于管理触发器和Job,例如启动某一任务,暂停某一任务等职责。
Job会被注册进Scheduler中,Scheduler通过调用Trigger来执行
//调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//将job与trigger都放入调度器中
scheduler.scheduleJob(jobDetail, trigger);
//启动定时任务
scheduler.start();
调度器的几种行为
- start 开始
- stop 停止
- pause暂停
- resume重试
(5) JobExecuteContext
看名字就知道,这是任务执行时的上下文,在实现Job方法重写excute时,作为参数传入任务中
public class MyJob implements Job
@Override
public void execute(JobExecutionContext context) throws JobExecutionException
//从context中获取job与trigger的名称
System.out.println("任务名为:"+context.getJobDetail().getKey()+"---触发器名为:"+context.getTrigger().getKey().getName());
-
当scheduler调用一个job,就会将jobExecuteContext传递给job的execute方法
-
job能够通过该对象访问到quartz运行时候的环境以及job本身的明细数据
(6) jobDataMap
jobDataMap用于存储Job实例的状态信息,在excute执行中,可以通过JobExecuteContext去获取map中的值
JobDetail jobDetail = JobBuilder.newJob(MyJob.class)
.withIdentity("任务1","组1")
//将map信息存放进jobDataMap中
.usingJobData("jobKey","jobValue")
.build();
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("触发器1","组1")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(1).repeatForever())
//将map信息存放进jobDataMap中
.usingJobData("triggerKey","triggerValue")
.build();
public class MyJob implements Job
@Override
public void execute(JobExecutionContext context) throws JobExecutionException
//从context中获取JobDataMap存储的信息
System.out.println(context.getJobDetail().getJobDataMap().get("jobKey"));
System.out.println(context.getTrigger().getJobDataMap().get("triggerKey"));
2. Job的并发执行
在Job任务每次执行时,JobDetail都会生成一个新的实例(包括内部的Job),例如间隔一秒执行一次任务,那么间隔一秒就会生成一个新的jobdetail实例
这是为了规避并发访问的问题,例比如一个任务要执行10秒,而调度算法是每秒钟触发1次,创建新的实例就是为了使每次调度都能执行,此时就有可能多个任务被并发执行
取消任务的并发执行
- @DisallowConcurrentExecution 禁止并发地执行同一个JobDetail
- @PersistJobDataAfterExecution 使JobDataMap跟随每一个任务执行而变动 (不使用的情况下新建jobdetail该Map也会新建)
这两个注解一般搭配使用
@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class MyJob implements Job
@Override
public void execute(JobExecutionContext context) throws JobExecutionException
//用户判断是否是同一个对象
System.out.println("jobDetail:"+System.identityHashCode(context.getJobDetail()));
System.out.println("job:"+System.identityHashCode(context.getJobInstance()));
注意点: @DisallowConcurrentExecution是对JobDetail实例生效,也如果定义两个JobDetail,引用一个Job类,是可以并发执行的
3. Quartz的线程池
定时器要调度多个定时任务,就得有一个线程池来进行任务的并发处理
当执行schedulerFactory.getScheduler()时,会初始化一个线程池SimpleThreadPool
在源码中,默认创建的线程池是一个SimpleThreadPool
-
此SimpleThreadPool是一个比较简单的线程池实现,只有线程数这一个属性,不像其他功能比较丰富的线程池有像核心线程数、最大线程数、队列大小等参数
-
此SimpleThreadPool的默认线程数为10
修改quartz线程池 的线程数量
可以在配置文件或配置类中进行配置
//代码配置
Properties prop = new Properties();
// 线程池配置
prop.put("org.quartz.threadPool.threadCount", "20");
SchedulerFactory schedulerFactory = new StdSchedulerFactory(prop);
Scheduler scheduler = schedulerFactory.getScheduler();
4. 任务错过触发的情况
列出了以下几种常见的情况
-
当job达到触发时间时,所有线程都被其他job占用,没有可用线程
-
在job需要触发的时间点,scheduler停止了(可能是手动调用pasue等方法,但也可能是意外停止的)
-
job使用了@ DisallowConcurrentExecution注解,job不能并发执行,当达到下一个job执行点的时候,上一个任务还没有完成
-
job指定了过去的开始执行时间,例如当前时间是8点00分0O秒,指定开始时间为7点00分00秒
任务错过的解决措施
(1) 增加线程池数,防止线程数不够的情况
(2) 设置任务执行的阈值,在到时间点后一段时间内仍可以执行
Properties prop = new Properties();
//1.线程池配置
prop.put("org.quartz.threadPool.threadCount", "20");
//2.设置任务执行时间阈值 单位是毫秒
prop.put("org.quartz.jobStore.misfireThreshold", "10000");
SchedulerFactory schedulerFactory = new StdSchedulerFactory(prop);
Scheduler scheduler = schedulerFactory.getScheduler();
(3) 设置错过的处理策略
具体的处理策略有很多,实际使用时可以自行百度
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("触发器1", "组1")
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(3)
.repeatForever()
// 设置错失触发后的调度策略
.withMisfireHandlingInstructionNowWithRemainingCount()
)
.build();
5. SpringBoot整合quartz
maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
简单使用方式
(1) 启动类上添加@EnableScheduling
(2) 配置定时任务@Scheduled
配置方式使用
(1) quartz配置
默认情况下,Quartz 会加载 classpath 下的 quartz.properties 作为配置文件。如果找不到,则会使用 quartz 框架自己 jar 包下 org/quartz 底下的 quartz.properties 文件
(2) quartz 配置类
/**
* @author ruoxi
* @createTime 2022/9/14 16:41
*/
@Configuration
public class QuartzConfig implements SchedulerFactoryBeanCustomizer
@Bean
public Properties properties() throws IOException
PropertiesFactoryBean propertiesFactoryBean = new PropertiesFactoryBean();
// 对quartz.properties文件进行读取
propertiesFactoryBean.setLocation(new ClassPathResource("/quartz.properties"));
// 在quartz.properties中的属性被读取并注入后再初始化对象
propertiesFactoryBean.afterPropertiesSet();
return propertiesFactoryBean.getObject();
@Bean
public SchedulerFactoryBean schedulerFactoryBean() throws IOException
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
schedulerFactoryBean.setQuartzProperties(properties());
return schedulerFactoryBean;
/**
* quartz初始化监听器
*/
@Bean
public QuartzInitializerListener executorListener()
return new QuartzInitializerListener();
/**
* 通过SchedulerFactoryBean获取Scheduler的实例
*/
@Bean
public Scheduler scheduler() throws IOException
return schedulerFactoryBean().getScheduler();
/**
* 使用阿里的druid作为数据库连接池
*/
@Override
public void customize(@NotNull SchedulerFactoryBean schedulerFactoryBean)
schedulerFactoryBean.setStartupDelay(2);
schedulerFactoryBean.setAutoStartup(true);
schedulerFactoryBean.setOverwriteExistingJobs(true);
(3)使用
/**
* @author ruoxi
* @createTime 2022/9/14 16:45
*/
@Service
@Log4j
public class QuartzService
@Autowired
private Scheduler scheduler;
/**
* 新增定时任务
*
* @param jName 任务名称
* @param jGroup 任务组
* @param tName 触发器名称
* @param tGroup 触发器组
* @param cron cron表达式
*/
public void addjob(String jName, String jGroup, String tName, String tGroup, String cron)
try
// 构建JobDetail
JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
.withIdentity(jName, jGroup)
.build();
// 按新的cronExpression表达式构建一个新的trigger
CronTrigger trigger = TriggerBuilder.newTrigger()
.withIdentity(tName, tGroup)
.startNow()
.withSchedule(CronScheduleBuilder.cronSchedule(cron))
.build();
// 启动调度器
scheduler.start();
scheduler.scheduleJob(jobDetail, trigger);
catch (Exception e)
System.out.println("创建定时任务失败" + e);
public void pausejob(String jName, String jGroup) throws SchedulerException
scheduler.pauseJob(JobKey.jobKey(jName, jGroup));
public void resumejob(String jName, String jGroup) throws SchedulerException
scheduler.resumeJob(JobKey.jobKey(jName, jGroup));
public void rescheduleJob(String jName, String jGroup, String cron) throws SchedulerException
TriggerKey triggerKey = TriggerKey.triggerKey(jName, jGroup);
// 表达式调度构建器
CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cron);
CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
// 按新的cronExpression表达式重新构建trigger
trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
// 按新的trigger重新设置job执行,重启触发器
scheduler.rescheduleJob(triggerKey, trigger);
public void deletejob(String jName, String jGroup) throws SchedulerException
scheduler.pauseTrigger(TriggerKey.triggerKey(jName, jGroup));
scheduler.unscheduleJob(TriggerKey.triggerKey(jName, jGroup));
scheduler.deleteJob(JobKey.jobKey(jName, jGroup));
具体的cron表达式可以看这篇博客 Cron表达式
以上是关于quartz学习笔记的主要内容,如果未能解决你的问题,请参考以下文章