Java - Quarz 定时任务(JobDetail & Job、Trigger、Scheduler)

JAVA - Quarz 定时任务

前言

在开发过程中,我们会用定时任务来执行一些操作,例如定时去捞取流水重试业务、定时去消息中间件获取消息等等相关需求

简单的定时任务实现可以借助Spring提供的 @Scheduled 注解 详细看 Spring 原理之 Scheduled

如果涉及到 定时任务的动态管理就需要使用到其他技术,下面介绍一下Quartz

Quartz是一个开源的任务日程管理系统, 由 OpenSymphony开源,同时它是一个功能丰富的任务调用系统,可创建简单或者复杂的几十、几百、甚至成千上万的job。除此之外,quartz调度器还支持JTA事务和集群。

Maven 的pom.xml文件引入相关依赖包

<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>2.3.2</version>
</dependency>

Quarz使用

1、总体介绍

  • org.quarz

请添加图片描述

说明:

Quarz 包中包含了三大要素

  • Scheduler:核心,任务调度器
  • Trigger:触发器,定义job执行的方式、间隔。
  • JobDetail & Job : 具体任务,用来定义任务执行的具体逻辑

2、JobDetail & Job

JobDetail & Job 是指具体任务,用来定义任务执行的具体逻辑

2.1 创建

一般配套使用,下面分别介绍 JobJobDetailJobBuilder

  • org.quarz.Job

只有一个方法,一般通过实现该接口 自定义自己的Job

public interface Job {
    /**
     * 执行
     */
    void execute(JobExecutionContext context)
        throws JobExecutionException;
}
  • org.quarz.JobDetail

常用的方法,自定义好Job之后,创建 JobDetail

public interface JobDetail extends Serializable, Cloneable {
	/**
	* JobDetail 的唯一身份
	*/
    public JobKey getKey();
	/**
	* 描述
	*/
    public String getDescription();
    /**
     * 获取到JobDetail关联的job class
     */
    public Class<? extends Job> getJobClass();
    /**
     * 与job相关的状态属性
     */
    public JobDataMap getJobDataMap();
    /**
     * JobDeatil创建使用的 JobBuilder
     */
    public JobBuilder getJobBuilder();
}

// JobKey 继承Key
// 主要有两个属性:
//private final String name;
//private final String group;  默认为 "DEFAULT"
public final class JobKey extends Key<JobKey> {
    private static final long serialVersionUID = -6073883950062574010L;
    public JobKey(String name) {
        super(name, null); // group 默认为 "DEFAULT"
    }
    public JobKey(String name, String group) {
        super(name, group);
    }
}

// JobDataMap:实质上就是HashMap来存储key、Value,用于多次执行Job的时候来跟踪Job的状态 JobDataMap中可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用其中的数据
  • org.quarz.JobBuilder

用于创建 JobDetail,常用的方法如下:

方法名称描述
newJob创建一个 JobBuilder ,指定Job的JobBuilder / 默认的JobBuilder
build利用 JobBuilder 创建 JobDetail
withIdentity创建 JobKey,多种重载方法,指定JobKey中的 name / group
usingJobData往默认的 JobDataMap(如果没设置) 中的 key 设置 value
setJobData设置 JobDataMap
public class JobBuilder {

    private JobKey key;
    private String description;
    private Class<? extends Job> jobClass;
    private boolean durability; //  如果一个job是非持久的,当没有活跃的trigger与之关联的时候,会被自动地从scheduler中删除。也就是说,非持久的job的生命期是由trigger的存在与否决定的;
    private boolean shouldRecover; // cheduler发生硬关闭(hard shutdown)(比如运行的进程崩溃了,或者关机了),则当scheduler重新启动的时候,该job是否被重新执行
    
    private JobDataMap jobDataMap = new JobDataMap();
    
    protected JobBuilder() {
    }
    
    /**
     * 创建 JobBuilder
     */
    public static JobBuilder newJob() {
        return new JobBuilder();
    }
    
    /**
     * 指定job的 JobBuilder
     */
    public static JobBuilder newJob(Class <? extends Job> jobClass) {
        JobBuilder b = new JobBuilder();
        b.ofType(jobClass);
        return b;
    }

    /**
     * 创建 JobDetail
     */
    public JobDetail build() {

        JobDetailImpl job = new JobDetailImpl();
        job.setJobClass(jobClass);
        job.setDescription(description);
        if(key == null)
            key = new JobKey(Key.createUniqueName(null), null);
        job.setKey(key); 
        job.setDurability(durability);
        job.setRequestsRecovery(shouldRecover);
        if(!jobDataMap.isEmpty())
            job.setJobDataMap(jobDataMap);
        return job;
    }
    
    /**
     * 设置 JobKey
     */
    public JobBuilder withIdentity(String name) {
        key = new JobKey(name, null);
        return this;
    }  
    
    /**
     * 设置 JobKey
     */
    public JobBuilder withIdentity(String name, String group) {
        key = new JobKey(name, group);
        return this;
    }
    
    /**
     * 设置 jobKey
     */
    public JobBuilder withIdentity(JobKey jobKey) {
        this.key = jobKey;
        return this;
    }
    
    /**
     * 设置 JobBuilder 的 job class 
     */
    public JobBuilder ofType(Class <? extends Job> jobClazz) {
        this.jobClass = jobClazz;
        return this;
    }

    
    // 设置 newJobDataMap,也有单独的 根据key 设置 value的多个重载方法
    public JobBuilder usingJobData(JobDataMap newJobDataMap) {
        jobDataMap.putAll(newJobDataMap);
        return this;
    }
    /**
     * 根据key 设置 value的多个重载方法
     */
    public JobBuilder usingJobData(String dataKey, Boolean value) {
        jobDataMap.put(dataKey, value);
        return this;
    }
    // 设置 JobDataMap
    public JobBuilder setJobData(JobDataMap newJobDataMap) {
        jobDataMap = newJobDataMap;
        return this;
    }
}

上面几种类/接口的关系:(Job与JobDetail通过 JobBuilder 产生联系)

  • JobBuilder :设置 Job , 设置 Job的状态 JobDataMap
  • JobBuilder: 创建 JobDetail 的唯一标识 JobKey
2.2 使用

1)关于Job的使用,一般会接口实现来完成我们自己的一些业务操作(如持久化、状态跟踪)

example:

// 简单使用
public class ExecuteJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("ExecuteJob 正在执行");
    }
}
// 复杂使用
public class ExecuteJob implements Job {
    // context 里面包含了很多方法,常用的如下
    // getScheduler:获取Scheduler
    // getTrigger:获取触发器Trigger
    //getJobDetail:获取JobDetail
    //getJobInstance:获取到Job
    // getMergedJobDataMap:获取 JobDetail中的JobDataMap和Trigger中的JobDataMap的并集,但是如果存在相同的数据,则后者会覆盖前者的值
    public void execute(JobExecutionContext context)
            throws JobExecutionException {

        // 获取 JobDetail 的 JobKey
        JobKey key = context.getJobDetail().getKey();
        // 获取 JobDetail中的JobDataMap和Trigger中的JobDataMap的并集
        JobDataMap dataMap = context.getMergedJobDataMap();
        // 获取创建 JobDetail 设置的值
        String AString = dataMap.getString("AString");
        float BFloatValue = dataMap.getFloat("BFloatValue");
        ArrayList CList = (ArrayList) dataMap.get("CList");

        System.out.println("Instance " + key + " of AString : " + AString + ", and BFloatValue is: " + BFloatValue);
    }
}

2)关于JobDeatil的使用

example:

// quartzJob 是与数据库相关的DO,用其id来构造JobKey保证唯一性
private JobDetail createJobDetail(QuartzJob quartzJob){
      String JOB_NAME = "TASK_"; // JobKey的名称前缀
      return JobBuilder.newJob(ExecuteJob.class). // 指定Job 的JobBuilder 
                withIdentity(JOB_NAME + quartzJob.getId()).build(); // 创建
}

 private JobDetail createJobDetail(QuartzJob quartzJob){
        String JOB_NAME = "TASK_";
        return JobBuilder.newJob(ExecuteJob.class).
                withIdentity(JOB_NAME + quartzJob.getId()) 
                .usingJobData("AString" , "i am a string ") // 指定 JobDataMap 的key value
                .usingJobData("BFloatValue" , 0F) // 指定 JobDataMap 的key value
                .build(); // 创建
    }

3、Trigger

Trigger 是指具体任务,触发器,定义job执行的方式、间隔。

3.1 创建

其跟 JobDetail 很像,一般也涉及到 Trigger 、 TriggerKey、TriggerBuilder

TriggerKey 与 JobKey 类似,都是有 name、group属性,用于唯一标识 Trigger,group默认为 “DEFAULT”

  • org.quarz.Trigger

Trigger 是一个接口,目前Quartz包里面已经有一些实现类

常用方法:

方法名称描述
getKey获取 TriggerKey
getJobKey获取 JobKey
getJobDataMap获取 JobDataMap
getStartTime
getEndTime
getPreviousFireTime
getNextFireTime
getFinalFireTime
getFireTimeAfter(Date afterTime)
触发器的时间
开始时间、截止时间
上次触发时间
下次触发时间
最后触发时间
在afterTime后的下次触发时间
getTriggerBuilder获取 TriggerBuiler
getScheduleBuilder获取 ScheduleBuilder

实现类:(不同的实现类上述方法实现略有不同,基本上都是很简单的Date、Calendar、TimeZone的一些设置与比较)

接口实现类描述
CalendarIntervalTriggerCalendarIntervalTriggerImpl触发任务 基于重复的日历时间间隔
CronTriggerCronTriggerImpl触发任务 基于Cron表达式
DailyTimeIntervalTriggerDailyTimeIntervalTriggerImpl触发任务 在给定的时间点,并且可以选择以指定的间隔重复
SimpleTriggerSimpleTriggerImpl触发任务 基于每天重复的时间间隔
  • org.quarz.TriggerBuilder

用于创建 Trigger,常用的方法如下:

方法名称描述
newTrigger创建一个 TriggerBuilder,new TriggerBuilder()
build利用 TriggerBuilder 创建 Trigger
withIdentity设置TriggerKey,多种重载方法,指定TriggerKey中的 name / group
forJob设置 JobKey,多种重载方法,指定JobKey中的 name / group
usingJobData往默认的 JobDataMap(如果没设置) 中的 key 设置 value
startNowtrigger 立即执行
startAt(Date triggerStartTime)在 指定时间 开始执行(设置 startTime)
endAt(Date triggerEndTime)在 指定时间 结束执行(设置 endTime)
withSchedule指定触发器Trigger的Scheduler
public class TriggerBuilder<T extends Trigger> {

    private TriggerKey key;
    private String description;
    private Date startTime = new Date();
    private Date endTime;
    private int priority = Trigger.DEFAULT_PRIORITY;
    private String calendarName;
    private JobKey jobKey;
    private JobDataMap jobDataMap = new JobDataMap();
    private ScheduleBuilder<?> scheduleBuilder = null;
    
    private TriggerBuilder() {
    }
   
    /**
     * 创建 TriggerBuilder
     */
    public static TriggerBuilder<Trigger> newTrigger() {
        return new TriggerBuilder<Trigger>();
    }
    
    /**
     * 创建 Trigger
     */
    @SuppressWarnings("unchecked")
    public T build() {
        if(scheduleBuilder == null)
            scheduleBuilder = SimpleScheduleBuilder.simpleSchedule();
        MutableTrigger trig = scheduleBuilder.build();
        
        trig.setCalendarName(calendarName);
        trig.setDescription(description);
        trig.setStartTime(startTime);
        trig.setEndTime(endTime);
        if(key == null)
            key = new TriggerKey(Key.createUniqueName(null), null);
        trig.setKey(key); 
        if(jobKey != null)
            trig.setJobKey(jobKey);
        trig.setPriority(priority);
        
        if(!jobDataMap.isEmpty())
            trig.setJobDataMap(jobDataMap);
        
        return (T) trig;
    }

    /** 设置 TriggerKey 三种重载方法
     * withIdentity(String name, String group)
     * withIdentity(TriggerKey triggerKey)
     * withIdentity(String name)
     */
    public TriggerBuilder<T> withIdentity(TriggerKey triggerKey) {
        this.key = triggerKey;
        return this;
    }

    /**
     * 设置开始执行时间
     */
    public TriggerBuilder<T> startAt(Date triggerStartTime) {
        this.startTime = triggerStartTime;
        return this;
    }
    
    /**
     * 立即执行
     */
    public TriggerBuilder<T> startNow() {
        this.startTime = new Date();
        return this;
    }

    /**
     * 设置结束时间
     */
    public TriggerBuilder<T> endAt(Date triggerEndTime) {
        this.endTime = triggerEndTime;
        return this;
    }

    /**
     * 设置 SchedulerBuiler 来创建 Trigger's schedule.
     */
    @SuppressWarnings("unchecked")
    public <SBT extends T> TriggerBuilder<SBT> withSchedule(ScheduleBuilder<SBT> schedBuilder) {
        this.scheduleBuilder = schedBuilder;
        return (TriggerBuilder<SBT>) this;
    }

    /**
      设置 JobKey 三种重载方法
     * forJob(JobKey keyOfJobToFire)
     * forJob(JobDetail jobDetail)
     * forJob(String jobName, String jobGroup)
     * forJob(String jobName)
     */
    public TriggerBuilder<T> forJob(JobDetail jobDetail) {
        JobKey k = jobDetail.getKey();
        if(k.getName() == null)
            throw new IllegalArgumentException("The given job has not yet had a name assigned to it.");
        this.jobKey = k;
        return this;
    }

    
    /**
     * 设置Trigger 的 JobDataMap 
     */
    public TriggerBuilder<T> usingJobData(JobDataMap newJobDataMap) {
        // add any existing data to this new map
        for(String dataKey: jobDataMap.keySet()) {
            newJobDataMap.put(dataKey, jobDataMap.get(dataKey));
        }
        jobDataMap = newJobDataMap; // set new map as the map to use
        return this;
    }
    /**
     * 根据key设置Value ,多种 重载方法,value的类型不同
     */
    public TriggerBuilder<T> usingJobData(String dataKey, Boolean value) {
        jobDataMap.put(dataKey, value);
        return this;
    }
  
}

3.2 使用

1)关于 Trigger 的使用,会根据

example:

//通过触发器名和cron 表达式创建 Trigger
// quartzJob 是与数据库相关的DO,用其id来构造TriggerKey保证唯一性
private Trigger createTrigger(QuartzJob quartzJob){
      String JOB_NAME = "TASK_"; // TriggerKey的 名称前缀
      return newTrigger()
        .withIdentity(JOB_NAME + quartzJob.getId()) // TriggerKey 的设置
        .startNow()  // 立即执行
        .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression())) // 根据Corne表达式执行Scheduler
        .build(); // 创建
}

4、Scheduler

scheduler 是quartz的核心所在,所有的任务都是通过scheduler开始

4.1 创建

Scheduler是一个接口,使用工厂模式创建,通过SchedulerFactory工厂类实现

  • org.quarz.SchedulerFactory
public interface SchedulerFactory {
	// 获取一个可用的Scheduler
    Scheduler getScheduler() throws SchedulerException;
	// 根据name获取Scheduler
    Scheduler getScheduler(String schedName) throws SchedulerException;
	// 获取所有的Scheduler
    Collection<Scheduler> getAllSchedulers() throws SchedulerException;
}

其有两个具体的实现类:**DirectSchedulerFactory **和 StdSchedulerFactory,核心的创建都是通过 SchedulerRepository

  • **DirectSchedulerFactory **:根据默认配置,单例创建Scheduler
  • StdSchedulerFactory:根据配置文件相关配置创建Scheduler,配置文件为当前工作目录下的 quartz.properties 属性文件。如果没有该文件/加载失败,会加载org/quartz包下的 quartz.properties 属性文件,也可自己制定其他的properties来设置参数值

DirectSchedulerFactory 核心方法:

方法名称方法描述
createScheduler实例方法创建一个Scheduler
由很多重载方法,根据参数不同
schedulerName(默认 SimpleQuartzScheduler
schedulerInstanceId(默认SIMPLE_NON_CLUSTERED
threadPool(需指定)
jobStore(需指定)
getScheduler实例方法还是调用getScheduler(String name)创建,name为默认的 SimpleQuartzScheduler
getScheduler(String name)实例方法返回一个指定name的Scheduler
getAllSchedulers实例方法获取到所有可用的 Scheduler

StdSchedulerFactory 常用方法

方法名称方法描述
getSchedulerName实例方法配置文件中org.quartz.scheduler.instanceName(DefaultQuartzScheduler)
若无配置默认为 QuartzScheduler
getDefaultScheduler静态方法返回一个默认的 Scheduler,内部还是调用 getScheduler()方法实现的
getScheduler实例方法利用 SchedulerRepository 创建一个Scheduler,如果没有才创建
getScheduler(String name)实例方法返回一个指定name的Scheduler
getAllSchedulers实例方法获取到所有可用的 Scheduler
  • org.quarz.quartz.properties
-----jar包自带的一些配置------
# scheduler的配置
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

# 线程池的相关配置
#线程池的实现类(一般使用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

org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
#设置是否为守护线程
org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore


-----扩展的一些配置------
#设置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


# 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

example:

Scheduler scheduler  = null;
scheduler = StdSchedulerFactory.getDefaultScheduler();
4.2 使用

常用的一些方法:

方法名称描述
start
startDelayed(int)
isStarted
开始 Scheduler 线程、立即开启/延迟开启、是否开启线程
shutdown
shutdown(boolean)
isShutdown
关闭Scheduler线程,立即关闭/等待所有job执行完关闭、是否关闭线程
scheduleJob设置定时任务 指定 JobDetail 与 trigger
triggerJob立即执行定时任务
unscheduleJob去除job的Trigger
rescheduleJob去除job的Trigger,重新配置新Trigger(之前的定时任务配置被删除)
resumeJob恢复执行定时任务
pauseJob暂停执行定时任务
deleteJob删除定时任务
1)、scheduleJob 设置定时任务 指定 job 与 trigger

涉及重载方法如下:

// 为指定的JobDetail 指定 Trigger 
Date scheduleJob(JobDetail jobDetail, Trigger trigger)
    throws SchedulerException;
/**
 * 为Scheduler默认的job 指定trigger
 */
Date scheduleJob(Trigger trigger) throws SchedulerException;
/**
 * 批量配置 triggersAndJobs
 * 如果job的key不唯一或者已经存在,replace 要设置为true,否则抛出异常
 */
void scheduleJobs(Map<JobDetail, Set<? extends Trigger>> triggersAndJobs, boolean replace) throws SchedulerException;

/**
 * 为指定的 jobDetail 配置多个Trigger 
 * 如果job的key不唯一或者已经存在,replace 要设置为true,否则抛出异常
 */
void scheduleJob(JobDetail jobDetail, Set<? extends Trigger> triggersForJob, boolean replace) throws SchedulerException;
2)、triggerJob 立即执行定时任务 指定 job 与 trigger

涉及重载方法如下:

/**
 * 立即执行,这里传入的是 JobDetail 的jobKey
 */
void triggerJob(JobKey jobKey)
    throws SchedulerException;

/**
 * 立即执行,这里传入的是 JobDetail 的jobKey
 * data 用来设置关联的 Trigger 的JobDataMap
 */
void triggerJob(JobKey jobKey, JobDataMap data)
    throws SchedulerException;
3)、unscheduleJob 去除job的Trigger
/**
 * 单个取消,这里传入 Trigger 的TriggerKey
 */
boolean unscheduleJob(TriggerKey triggerKey)
    throws SchedulerException;

/**
 * 批量取消,这里传入 Trigger 的TriggerKey List
 */
boolean unscheduleJobs(List<TriggerKey> triggerKeys)
    throws SchedulerException;
4)、rescheduleJob 去除job的Trigger 重新配置新Trigger
/**
 * 删除 triggerKey 的Trigger ,然后配置一个新的newTrigger , 对同一个Job操作
 */
Date rescheduleJob(TriggerKey triggerKey, Trigger newTrigger) 
    throws SchedulerException;
5)、pauseJob、resumeJob 暂停/恢复job
/**
 * 根据 jobKey 暂停 Job
 */
void pauseJob(JobKey jobKey)
    throws SchedulerException;

/**
 * 根据 Group来 暂停 一组Job
 */
void pauseJobs(GroupMatcher<JobKey> matcher) throws SchedulerException;

/**
 * 根据 jobKey 恢复 Job
 */
void resumeJob(JobKey jobKey)
    throws SchedulerException;

/**
 * 根据 Group来 恢复 一组Job
 */
void resumeJobs(GroupMatcher<JobKey> matcher) throws SchedulerException;
6)、deleteJob 删除job
/**
 * 根据 jobKey 删除 Job
 */
boolean deleteJob(JobKey jobKey)
    throws SchedulerException;

/**
 * 根据 jobKey 批量删除 Job
 */
boolean deleteJobs(List<JobKey> jobKeys)
    throws SchedulerException;

example:

public class QuartzManager {

	// 增加定时任务
    public void addJob(QuartzJob quartzJob) {
        try {
            // 构建job信息
            JobDetail jobDetail = createJobDetail(quartzJob);

            //通过触发器名和cron 表达式创建 Trigger
            Trigger cronTrigger = createTrigger(quartzJob);

            cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob);

            //重置启动时间
            ((CronTriggerImpl) cronTrigger).setStartTime(new Date());

            // 创建Scheduler
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            
            // 启动线程 ,非常重要
            scheduler.start(); 
            
            //执行定时任务
            scheduler.scheduleJob(jobDetail, cronTrigger);

        } catch (Exception e) {
            log.error("创建定时任务失败", e);
        }
    }
	// 创建JobDetail 
    private JobDetail createJobDetail(QuartzJob quartzJob) {
        String JOB_NAME = "TASK_";
        return JobBuilder.newJob(ExecuteJob.class).
                withIdentity(JOB_NAME + quartzJob.getId())
                .usingJobData("AString", "i am a string ")
                .usingJobData("BFloatValue", 0F)
                .build();
    }
    // 创建Trigger 
    private Trigger createTrigger(QuartzJob quartzJob) {
        String JOB_NAME = "TASK_";
        return newTrigger()
                .withIdentity(JOB_NAME + quartzJob.getId())
                .startNow()
                .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()))
                .build();
    }
}
//  项目 runner
@Component
@RequiredArgsConstructor
public class JobRunner implements ApplicationRunner {
    private static final Logger log = LoggerFactory.getLogger(JobRunner.class);
    private final QuartzManager quartzManager;

    /**
     * 项目启动时重新激活启用的定时任务
     *
     * @param applicationArguments /
     */
    @Override
    public void run(ApplicationArguments applicationArguments) {
        log.info("--------------------注入系统定时任务------------------");
        // 一般这里可以从数据库获取到大量的QuartzJob ,然后将这些都注入到定时任务中
        QuartzJob job = new QuartzJob();
        job.setId(1l);
        job.setCronExpression("0/1 * * * * ?");
        quartzManager.addJob(job);
        log.info("--------------------定时任务注入完成------------------");
    }
}

// QuartzJob
@Getter
@Setter
public class QuartzJob  implements Serializable {
    private Long id;
    private String cronExpression;
}

# 输出
Instance DEFAULT.TASK_1 of AString : i am a string , and BFloatValue is: 0.0
Instance DEFAULT.TASK_1 of AString : i am a string , and BFloatValue is: 0.0
Instance DEFAULT.TASK_1 of AString : i am a string , and BFloatValue is: 0.0
Instance DEFAULT.TASK_1 of AString : i am a string , and BFloatValue is: 0.0
Instance DEFAULT.TASK_1 of AString : i am a string , and BFloatValue is: 0.0
Instance DEFAULT.TASK_1 of AString : i am a string , and BFloatValue is: 0.0

总结

简单的使用定时任务 基本上使用Spring的Schedule来实现功能即可,但是其不能动态的管理

如果需要动态的管理,可以用 Quartz 来实现,常见的一些方法列举出来了,具体使用还要依靠具体使用,大家也可去看看源码,简单易懂,底层原理等其他博客~

一个封装小例子:

package study;
import lombok.extern.slf4j.Slf4j;
import me.quartz.domain.QuartzJob;
import me.quartz.domain.ExecuteJob;
import org.quartz.*;
import org.quartz.impl.triggers.CronTriggerImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Date;
import static org.quartz.TriggerBuilder.newTrigger;

@Slf4j
@Component
public class QuartzManage {

    private static final String JOB_NAME = "TASK_";

    @Resource
    private Scheduler scheduler;

    public void addJob(QuartzJob quartzJob){
        try {
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ExecuteJob.class).
                    withIdentity(JOB_NAME + quartzJob.getId()).build();

            //通过触发器名和cron 表达式创建 Trigger
            Trigger cronTrigger = newTrigger()
                    .withIdentity(JOB_NAME + quartzJob.getId())
                    .startNow()
                    .withSchedule(CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression()))
                    .build();

            cronTrigger.getJobDataMap().put(QuartzJob.JOB_KEY, quartzJob);

            //重置启动时间
            ((CronTriggerImpl)cronTrigger).setStartTime(new Date());

            //执行定时任务
            scheduler.scheduleJob(jobDetail,cronTrigger);

            // 暂停任务
            if (quartzJob.getIsPause()) {
                pauseJob(quartzJob);
            }
        } catch (Exception e){
            log.error("创建定时任务失败", e);
        }
    }

    /**
     * 更新job cron表达式
     * @param quartzJob /
     */
    public void updateJobCron(QuartzJob quartzJob){
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 如果不存在则创建一个定时任务
            if(trigger == null){
                addJob(quartzJob);
                trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            }
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(quartzJob.getCronExpression());
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
            //重置启动时间
            ((CronTriggerImpl)trigger).setStartTime(new Date());
            trigger.getJobDataMap().put(QuartzJob.JOB_KEY,quartzJob);

            scheduler.rescheduleJob(triggerKey, trigger);
            // 暂停任务
            if (quartzJob.getIsPause()) {
                pauseJob(quartzJob);
            }
        } catch (Exception e){
            log.error("更新定时任务失败", e);
        }

    }

    /**
     * 删除一个job
     * @param quartzJob /
     */
    public void deleteJob(QuartzJob quartzJob){
        try {
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.pauseJob(jobKey);
            scheduler.deleteJob(jobKey);
        } catch (Exception e){
            log.error("删除定时任务失败", e);
        }
    }

    /**
     * 恢复一个job
     * @param quartzJob /
     */
    public void resumeJob(QuartzJob quartzJob){
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 如果不存在则创建一个定时任务
            if(trigger == null) {
                addJob(quartzJob);
            }
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.resumeJob(jobKey);
        } catch (Exception e){
            log.error("恢复定时任务失败", e);
        }
    }

    /**
     * 立即执行job
     * @param quartzJob /
     */
    public void runJobNow(QuartzJob quartzJob){
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(JOB_NAME + quartzJob.getId());
            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
            // 如果不存在则创建一个定时任务
            if(trigger == null) {
                addJob(quartzJob);
            }
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(QuartzJob.JOB_KEY, quartzJob);
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.triggerJob(jobKey,dataMap);
        } catch (Exception e){
            log.error("定时任务执行失败", e);
        }
    }

    /**
     * 暂停一个job
     * @param quartzJob /
     */
    public void pauseJob(QuartzJob quartzJob){
        try {
            JobKey jobKey = JobKey.jobKey(JOB_NAME + quartzJob.getId());
            scheduler.pauseJob(jobKey);
        } catch (Exception e){
        }
    }
}
  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值