任务调度框架 Quartz 一文读懂

Posted 在奋斗的大道

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了任务调度框架 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包

        <!-- 核心包 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>
        <!-- 工具包 -->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz-jobs</artifactId>
            <version>2.3.0</version>
        </dependency>

 第二步:自定义Job

package com.zzg.quartz.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class MyJob implements Job 
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException 
        System.out.println("定时任务执行了!");
    

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

package com.zzg.quartz;

import com.zzg.quartz.job.MyJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.concurrent.TimeUnit;

import static org.quartz.SimpleScheduleBuilder.simpleSchedule;

/**
 * 核心步骤说明:
 * 1.创建调度器 Scheduler
 * 
 * 2.创建JobDetail实例,并与MyJob类绑定
 * 
 * 3.构建Trigger实例,指定时间执行
 * 
 * 4.执行,开启调度器
 */
public class QuartzTest 
    public static void main(String[] args) throws SchedulerException, InterruptedException 
        // 1.创建调度器 Scheduler
        SchedulerFactory factory = new StdSchedulerFactory();
        Scheduler scheduler = factory.getScheduler();

        // 2.创建JobDetail实例,并与MyJob类绑定(Job执行内容)
        JobDetail job = JobBuilder.newJob(MyJob.class)
                .withIdentity("job1", "group1")
                .build();

        // 3.构建Trigger实例,每隔30s执行一次
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1")
                .startNow()
                .withSchedule(simpleSchedule()
                        .withIntervalInSeconds(30)
                        .repeatForever())
                .build();

        // 4.执行,开启调度器
        scheduler.scheduleJob(job, trigger);
        System.out.println(System.currentTimeMillis());
        scheduler.start();

        //主线程睡眠1分钟,然后关闭调度器
        TimeUnit.MINUTES.sleep(1);
        scheduler.shutdown();
        System.out.println(System.currentTimeMillis());
    

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 参数获取。

public interface Job 
    void execute(JobExecutionContext context)
        throws JobExecutionException;

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

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

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

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

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

package com.zzg.quartz.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class MyJob implements Job 
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException 
        Object group = context.getTrigger().getJobDataMap().get("trigger");
        Object group1 = context.getJobDetail().getJobDataMap().get("quartz");
        System.out.println("定时任务执行了!");
    

Job状态参数 

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

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

自定义MyJob

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

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

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

Trigger

定时启动/关闭

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

Calendar instance = Calendar.getInstance();
Date startTime = instance.getTime();
instance.add(Calendar.MINUTE, 1);
Date endTime = instance.getTime();

// 3.构建Trigger实例
Trigger trigger = TriggerBuilder.newTrigger()
    .withIdentity("trigger1", "group1")
    // 开始时间
    .startAt(startTime)
    // 结束时间
    .endAt(endTime)
    .build();

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

public void execute(JobExecutionContext context) throws JobExecutionException 
    System.out.println("任务执行。。。");
    System.out.println(context.getTrigger().getStartTime());
    System.out.println(context.getTrigger().getEndTime());

SimpleTrigger

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

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

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

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

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

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

  这个省略...

CronTigger

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

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

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

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

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

Quartz 与Spring Boot 集成

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

以上是关于任务调度框架 Quartz 一文读懂的主要内容,如果未能解决你的问题,请参考以下文章

一文揭秘定时任务调度框架quartz

一文读懂Linux任务间调度原理和整个执行过程

Quartz任务调度框架

一文搞懂springboot定时任务

Java任务调度框架Quartz教程实例

Quartz任务调度框架使用