• 任务调度框架 Quartz 一文读懂


    1、Quartz 简介

    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

    2、Quartz 快速入门

    Quartz 核心概念

    Quartz 的核心类由以下三部分构成:

    • 任务 Job : 需要实现的任务类,实现 execute() 方法,执行后完成任务。

    • 触发器 Trigger : 包括 SimpleTrigger 和 CronTrigger

    • 调度器 Scheduler : 任务调度器,负责基于 Trigger触发器,来执行 Job任务。

    关系结构图:

     

     Quartz 快速入门

    第一步:创建Maven项目,添加Quartz 定时任务框架依赖Jar包

    1. <dependency>
    2. <groupId>org.quartz-schedulergroupId>
    3. <artifactId>quartzartifactId>
    4. <version>2.3.0version>
    5. dependency>
    6. <dependency>
    7. <groupId>org.quartz-schedulergroupId>
    8. <artifactId>quartz-jobsartifactId>
    9. <version>2.3.0version>
    10. dependency>

     第二步:自定义Job

    1. package com.zzg.quartz.job;
    2. import org.quartz.Job;
    3. import org.quartz.JobExecutionContext;
    4. import org.quartz.JobExecutionException;
    5. public class MyJob implements Job {
    6. @Override
    7. public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    8. System.out.println("定时任务执行了!");
    9. }
    10. }

    第三步:测试方法实现创建调度器、jobDetail 实例、trigger 实例、执行。

    1. package com.zzg.quartz;
    2. import com.zzg.quartz.job.MyJob;
    3. import org.quartz.*;
    4. import org.quartz.impl.StdSchedulerFactory;
    5. import java.util.concurrent.TimeUnit;
    6. import static org.quartz.SimpleScheduleBuilder.simpleSchedule;
    7. /**
    8. * 核心步骤说明:
    9. * 1.创建调度器 Scheduler
    10. *
    11. * 2.创建JobDetail实例,并与MyJob类绑定
    12. *
    13. * 3.构建Trigger实例,指定时间执行
    14. *
    15. * 4.执行,开启调度器
    16. */
    17. public class QuartzTest {
    18. public static void main(String[] args) throws SchedulerException, InterruptedException {
    19. // 1.创建调度器 Scheduler
    20. SchedulerFactory factory = new StdSchedulerFactory();
    21. Scheduler scheduler = factory.getScheduler();
    22. // 2.创建JobDetail实例,并与MyJob类绑定(Job执行内容)
    23. JobDetail job = JobBuilder.newJob(MyJob.class)
    24. .withIdentity("job1", "group1")
    25. .build();
    26. // 3.构建Trigger实例,每隔30s执行一次
    27. Trigger trigger = TriggerBuilder.newTrigger()
    28. .withIdentity("trigger1", "group1")
    29. .startNow()
    30. .withSchedule(simpleSchedule()
    31. .withIntervalInSeconds(30)
    32. .repeatForever())
    33. .build();
    34. // 4.执行,开启调度器
    35. scheduler.scheduleJob(job, trigger);
    36. System.out.println(System.currentTimeMillis());
    37. scheduler.start();
    38. //主线程睡眠1分钟,然后关闭调度器
    39. TimeUnit.MINUTES.sleep(1);
    40. scheduler.shutdown();
    41. System.out.println(System.currentTimeMillis());
    42. }
    43. }

    Quartz 核心类

    JobDetail

    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实例,这样就可以 规避并发访问 的问题。

    JobExecutionContext 

    • 当 Scheduler 调用一个 job,就会将 JobExecutionContext 传递给 Job 的 execute() 方法;

    • Job 能通过 JobExecutionContext 对象访问到 Quartz 运行时候的环境以及 Job 本身的明细数据。

    任务实现的 execute() 方法,可以通过 context 参数获取。

    1. public interface Job {
    2. void execute(JobExecutionContext context)
    3. throws JobExecutionException;
    4. }

    JobDetail 建造过程中,可以使用如下方法:

    1. // 2.创建JobDetail实例,并与MyJob类绑定(Job执行内容)
    2. JobDetail job = JobBuilder.newJob(MyJob.class)
    3. .withIdentity("job1", "group1")
    4. // 定义任务添加自定义参数
    5. .usingJobData("quartz", "quartz 自定义参数")
    6. .build();

    在 Trigger建造过程中,可以使用如下方法:

    1. // 3.构建Trigger实例,每隔30s执行一次
    2. Trigger trigger = TriggerBuilder.newTrigger()
    3. .withIdentity("trigger1", "group1")
    4. // Trigger 添加自定义参数
    5. .usingJobData("trigger", "trigger 自定义参数")
    6. .startNow()
    7. .withSchedule(simpleSchedule()
    8. .withIntervalInSeconds(30)
    9. .repeatForever())
    10. .build();

    在 自定义MyJob中的execute 方法中获取:

    1. package com.zzg.quartz.job;
    2. import org.quartz.Job;
    3. import org.quartz.JobExecutionContext;
    4. import org.quartz.JobExecutionException;
    5. public class MyJob implements Job {
    6. @Override
    7. public void execute(JobExecutionContext context) throws JobExecutionException {
    8. Object group = context.getTrigger().getJobDataMap().get("trigger");
    9. Object group1 = context.getJobDetail().getJobDataMap().get("quartz");
    10. System.out.println("定时任务执行了!");
    11. }
    12. }

    Job状态参数 

    有状态的 job 可以理解为多次 job调用期间可以持有一些状态信息,这些状态信息存储在 JobDataMap 中。

    而默认的无状态 job,每次调用时都会创建一个新的 JobDataMap

    自定义MyJob

    1. //多次调用 Job 的时候,将参数保留在 JobDataMap
    2. @PersistJobDataAfterExecution
    3. public class JobStatus implements Job {
    4. @Override
    5. public void execute(JobExecutionContext context) throws JobExecutionException {
    6. long count = (long) context.getJobDetail().getJobDataMap().get("count");
    7. System.out.println("当前执行,第" + count + "次");
    8. context.getJobDetail().getJobDataMap().put("count", ++count);
    9. }
    10. }

    在 JobDetail 建造过程中,携带拓展参数

    1. JobDetail job = JobBuilder.newJob(JobStatus.class)
    2. .withIdentity("statusJob", "group1")
    3. .usingJobData("count", 1L)
    4. .build();

    Trigger

    定时启动/关闭

    Trigger 可以设置任务的开始结束时间, Scheduler 会根据参数进行触发。

    1. Calendar instance = Calendar.getInstance();
    2. Date startTime = instance.getTime();
    3. instance.add(Calendar.MINUTE, 1);
    4. Date endTime = instance.getTime();
    5. // 3.构建Trigger实例
    6. Trigger trigger = TriggerBuilder.newTrigger()
    7. .withIdentity("trigger1", "group1")
    8. // 开始时间
    9. .startAt(startTime)
    10. // 结束时间
    11. .endAt(endTime)
    12. .build();

    在 job 中也能拿到对应的时间,并进行业务判断

    1. public void execute(JobExecutionContext context) throws JobExecutionException {
    2. System.out.println("任务执行。。。");
    3. System.out.println(context.getTrigger().getStartTime());
    4. System.out.println(context.getTrigger().getEndTime());
    5. }

    SimpleTrigger

    比较简单的一类触发器,用它能实现很多基础的应用。使用它的主要场景包括:

    • 在指定时间段内,执行一次任务

    最基础的 Trigger 不设置循环,设置开始时间。

    • 在指定时间段内,循环执行任务

    在 快速入门代码基础上加上循环间隔。可以指定 永远循环、运行指定次数

    1. TriggerBuilder.newTrigger()
    2. .withSchedule(SimpleScheduleBuilder
    3. .simpleSchedule()
    4. .withIntervalInSeconds(30)
    5. .repeatForever())
    6. withRepeatCount(count)` 是重复次数,实际运行次数为 `count+1
    7. TriggerBuilder.newTrigger()
    8. .withSchedule(SimpleScheduleBuilder
    9. .simpleSchedule()
    10. .withIntervalInSeconds(30)
    11. .withRepeatCount(5))
    • 立即开始,指定时间结束

      这个省略...

    CronTigger

    CronTrigger 是基于日历的任务调度器,在实际应用中更加常用。

    虽然很常用,但是知识点都一样,只是可以通过表达式来设置时间而已。

    使用方式就是绑定调度器时换一下:

    TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("* * * * * ?"))

    Cron 表达式请参考:Cron表达式学习

    Quartz 与Spring Boot 集成

    请参考:Spring Boot2.x + Quartz 定时任务模块

  • 相关阅读:
    护眼台灯哪个牌子最好?消费者信赖之选护眼灯十大品牌推荐
    太坑了,我竟然从RocketMQ源码中扒出了7种导致消息重复消费的原因
    Linux 中安装MySQL
    x6.js bug记录-流程图json数据导入进来之后拖拽节点,节点直接飞走了
    python经典百题之寻找完数
    【机器学习】集成学习:使用scikitLearn中的BaggingClassifier实现bagging和pasting策略
    Mysql-题目02
    pygame的freetype模块
    在阿里云的函数计算上部署
    漏洞补丁:漏洞命名(CVE和CNNVD)及补丁查找
  • 原文地址:https://blog.csdn.net/zhouzhiwengang/article/details/126159139