Quartz任务调度

快速入门#

任务类

public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext arg0) throws JobExecutionException {
        ystem.out.println(new Date());
    }
}

任务调度类

public class HelloSchedulerDemo {

    public static void main(String[] args) throws Exception {
        // 1、调度器(Scheduler),从工厂中获取调度的实例
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

        // 2、任务实例(JobDetail)
        JobDetail jobDetail = JobBuilder.newJob() 
                .withIdentity("job1", "group1") // 参数1:任务的名称(唯一);参数2:任务组的名称
                .build();

        // 3、触发器(Trigger)
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1") // 参数1:触发器的名称(唯一);参数2:触发器组的名称
                .startNow() // 马上启动触发器
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                                .withIntervalInSeconds(2)
                                .repeatForever()) // 每2秒重复执行一次
                .build();

        // 4、让调度器关联任务和触发器
        scheduler.scheduleJob(jobDetail, trigger);

        // 5、启动
        scheduler.start();
        // 关闭
        //scheduler.shutdown();
    }

}

核心概念#

通过代码可以得到以下几个核心概念

Job
自定义的任务类都必须实现org.quartz.job接口,并重写其中的execute()方法

Trigger
可以简单理解为何时执行,包含 SimplerTrigger 和 CronTrigger 两种

Scheduler
将任务 Job 及触发器 Trigger 整合起来,负责基于 Trigger 设定的时间来执行 Job

Job和JobDetail#

Job每次调度器执行Job时,它在调用execute方法前会创建一个新的 Job 实例,当调用完成后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收

JobDetail为Job实例提供了许多设置属性,调度器需要借助JobDetail对象来添加Job实例

JobExecutionContext#

  • 当 Scheduler 调用一个 Job,就会将 JobExecutionContext 传递给 Job 的 execute() 方法
  • Job 能通过 JobExecutionContext 对象访问到 Quartz 运行时候的环境以及 Job 本身的明细数据
  • 仅在Job被调用时才能通过JobExecutionContext获取运行化境数据(因为只有这时候JobExecutionContext才被传递到execute方法)

JobDataMap#

初始化信息

// 将 jobDataMap1 中数据存入 JobDetail 中
// 将 jobDataMap2 中数据存入 Trigger 中
public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        //1. 调度器(Scheduler)
        Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler();
		
	JobDataMap jobDataMap1 = new JobDataMap();
        jobDataMap1.put("message", "JobDetailMessage");

        //2. 任务实例(JobDetail)
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "jobGroup1")
                .usingJobData(jobDataMap1) // 将 JobDataMap 放入 JobDetail 中
                .build();

        JobDataMap jobDataMap2 = new JobDataMap();
        jobDataMap2.put("message", "TriggerMessage");

        //3. 触发器(Trigger)
        Trigger trigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "triggerGroup1")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                                .withIntervalInSeconds(2)
                                .repeatForever())
                                .endAt(new Date(new Date().getTime() + 3000L))
                .usingJobData(jobDataMap2) // 将 JobDataMap 放入 Trigger 中
                .build();

        defaultScheduler.scheduleJob(jobDetail, trigger);
        defaultScheduler.start();
    }
}

使用map获取#

  • 用来存储特定Job实例的状态信息
  • 存储在JobExecutionContext
// 获取之前存入 map 中的数据
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
    	System.out.println(jobExecutionContext.getTrigger().getJobDataMap().get("message")); 
        System.out.println(jobExecutionContext.getJobDetail().getJobDataMap().get("message")); 

        System.out.println(new Date());
    }
}

使用setter获取#

Job实现类中添加setter方法对应JobDataMap的键值,Quartz框架默认的JobFactory实现类在初始化Job实例对象时会自动调用这些setter方法

@Data
public class HelloJob implements Job {
    private String message;

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println(message); //这里Trigger会对JobDetail进行覆盖
        System.out.println(new Date());
    }
}

需要注意的是:如果遇到同名key,Trigger中JobDataMap中的值会自动覆盖JobDetail中JobDataMap的值

Job的状态#

Job有一个StatefulJob子接口,代表有状态的任务,该接口是一个没有方法的标签接口,其目的是让Quartz知道任务的类型,以便采用不同的执行方案。无状态任务在执行时拥有自己的JobDataMap拷贝,对JobDataMap的更改不会影响下次的执行。而有状态任务共享共享同一个JobDataMap实例,每次任务执行对JobDataMap所做的更改会保存下来,后面的执行可以看到这个更改,也即每次执行任务后都会对后面的执行发生影响

正因为这个原因,无状态的Job可以并发执行,而有状态的StatefulJob不能并发执行,这意味着如果前次的StatefulJob还没有执行完毕,下一次的任务将阻塞等待,直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素,程序往往拥有更高的复杂度,因此除非必要,应该尽量使用无状态的Job

SimpleTrigger和CronTrigger#

SimpleTrigger#

它是为那种需要在特定的日期/时间启动,且以一个可能的间隔时间重复执行 n 次的 Job 所设计的,通过观察它的api我们就能明显的看出这个特点

  • startNow()Scheduler
    • 开始执行时,触发器也即执行
  • startAt(new Date())
    • 在指定时间开始执行
  • withIntervalInSeconds(2)
    • 指定间隔执行,方法名对应时间单位
  • repeatForever()
    • 永远执行
  • withRepeatCount(3)
    • 执行次数
  • endAt(new Date())
    • 停止时间

CronTrigger#

像日程表一样,通常基于日历进行作业,需要用cron表达式进行初始化

用这个生成吧,好使 http://cron.ciding.cc/

Scheduler#

再多废话几句:Trigger和JobDetail可以注册到Scheduler中, 两者在Scheduler中拥有各自的组及名称, 组及名称是Scheduler查找定位容器中某一对象的依据, Trigger的组及名称必须唯一, JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法, 允许外部通过组及名称访问和控制容器中Trigger和JobDetail

常用的方法

scheduler.scheduleJob(jobDetail, trigger);                      //绑定jobDetail与trigger
scheduler.checkExists(JobKey.jobKey(name, group))	        //检查JobDetail是否存在
scheduler.checkExists(TriggerKey.triggerKey(name, group))	//检查Trigger是否存在
scheduler.deleteJob(JobKey.jobKey(name, group))		        //删除jobDetail

监听器#

包含JobListener、TriggerListener、SchedulerListener三种,但是在了解这三种监听器之前,需要先了解从另一个维度对监听器的分类:全局监听器和非全局监听器

  • 全局监听器能够接收到所有的Job/Trigger的事件通知
  • 而非全局监听器只能接收到在其上注册的Job或者Trigger的事件,不在其上注册的Job或Trigger则不会进行监听

挖坑...以后补上监听器这块

实例#

包括添加、修改触发时间、删除

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzManager {
    private static SchedulerFactory schedulerFactory = new StdSchedulerFactory();

    /**
     * @description: 添加一个定时任务
     * @param: jobName     任务名
     * jobGroupName        任务组名
     * triggerName         触发器名
     * triggerGroupName    触发器组名
     * jobClass            任务
     * cron                计划时间
     * @return:
     * @author: lyy
     * @date: 2022/7/1
     */
    public static void addJob(String jobName,
                              String jobGroupName,
                              String triggerName,
                              String triggerGroupName,
                              Class jobClass,
                              String cron) throws Exception {
        Scheduler scheduler = schedulerFactory.getScheduler();
//        JobDetail jobDetail= JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();
//        TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();
//        triggerBuilder.withIdentity(triggerName, triggerGroupName);
//        triggerBuilder.startNow();
        //任务
        JobDetail jobDetail = JobBuilder.newJob().withIdentity(jobName, jobGroupName).build();
        //触发器
        CronTrigger trigger = TriggerBuilder.newTrigger()
                .withIdentity(triggerName, triggerGroupName)
                .startNow().withSchedule(CronScheduleBuilder.cronSchedule(cron))
                .build();
        //绑定
        scheduler.scheduleJob(jobDetail, trigger);
        if (!scheduler.isShutdown()) {
            scheduler.start();
        }
    }

    /**
     * @description: 修改一个任务的触发时间
     * @param: [jobName, jobGroupName, triggerName, triggerGroupName, cron]
     * @return:
     * @author: lyy
     * @date: 2022/7/1
    */
    public static void modifyJobTime(String jobName,
                                     String jobGroupName,
                                     String triggerName,
                                     String triggerGroupName,
                                     String cron) throws Exception
    {
        Scheduler scheduler = schedulerFactory.getScheduler();
        //triggerKey用于唯一标识一个trigger
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
        //获取原有的
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        if(trigger == null){
            return ;
        }
        //获取原有触发时间
        String oldTime = trigger.getCronExpression();
        //todo:考虑是否加非空判断
        if(!oldTime.equalsIgnoreCase(cron)){
            //重新构造触发器赋值给trigger
            trigger = TriggerBuilder.newTrigger()
                    .withIdentity(triggerName, triggerGroupName)
                    .startNow().withSchedule(CronScheduleBuilder.cronSchedule(cron))
                    .build();
            //根据triggerKey(唯一标识)对原有trigger进行替换
            scheduler.rescheduleJob(triggerKey, trigger);
        }
    }

    /**
     * @description: 移除任务
     * @param: [jobName, jobGroupName, triggerName, triggerGroupName]
     * @return:
     * @author: lyy
     * @date: 2022/7/1
    */
    public static void removeJob(String jobName,
                                 String jobGroupName,
                                 String triggerName,
                                 String triggerGroupName) throws Exception
    {
        Scheduler scheduler = schedulerFactory.getScheduler();
        TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
        //停止触发器
        scheduler.pauseTrigger(triggerKey);
        //移除触发器
        scheduler.unscheduleJob(triggerKey);
        //删除任务
        scheduler.deleteJob(JobKey.jobKey(jobName,jobGroupName));
    }

    //开始和删除所有任务比较简单就不代码实现了,就是scheduler调用start()方法和shutdown()方法
}



本文内容参考
Quartz基础+实例
Quartz分布式任务调度

posted @   colee51666  阅读(108)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示
主题色彩
西雅图
11°
22:09发布
西雅图
22:09发布
11°
多云
西北风
2级
空气质量
相对湿度
69%
今天
5°/11°
周五
2°/13°
周六
小雨
6°/14°