Quartz定时任务调度框架基础入门

一、简介

Quartz:http://www.quartz-scheduler.org/

是一个定时任务调度框架。比如你遇到这样的问题:

  • 想在30分钟后,查看订单是否支付,未支付则取消订单
  • 想在每月29号,信用卡自动还款
  • 想定时在某个时间,去做某件事(任务)。

Quartz是要做定时任务的调度,设置好触发时间规则,以及相应的任务(Job)即可。

如果应用程序需要在给定时间执行任务,或者如果系统有连续维护作业,那么Quartz是理想的解决方案。 使用Quartz作业调度应用的示例: 驱动处理工作流程:作为一个新的订单被初始化放置,调度作业到在正好两个小时内,它将检查订单的状态,如果订单确认消息尚未收到命令触发警告通知,以及改变订单的状态为“等待的干预”。 系统维护:调度工作给数据库的内容,每个工
作日(节假日除外平日)在11:30 PM转储到一个XML文件中。 在应用程序内提供提醒服务。

Quartz 可以运行嵌入在另一个独立式应用程序 Quartz 可以在应用程序服务器(或servlet容器)内被实例化,并且参与XA事务 Quartz 可以作为一个独立的程序运行(其自己的Java虚拟机内),可以通过RMI使用 Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行Quartz可以被持久化

二、Quartz使用

1、导入依赖

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

2、定义job

public class FirstJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("任务正在执行中......");
        //获取到执行的任务对象
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        //获取jobDetail的名称
        String name = jobDetail.getKey().getName();
        String group = jobDetail.getKey().getGroup();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        String string = jobDataMap.getString("name");
        System.out.println("正在执行的任务名:"+name+",正在执行任务所在组"+group+",任务数据name的值为:"+string);
    }
}

3、测试任务调度运行job

public class QuartzTest {
    public static void main(String[] args) {
        try {
            //1、创建任务调度器,设置执行的时间规则
            Trigger trigger = TriggerBuilder.newTrigger()//此处会先检查加载quartz.properties配置文件,如果没有执行默认规则
                    //设计trigger标识
                    .withIdentity("trigger1","group1")
                    .startNow() //启动立即执行
                    //设置Schedule模型
                    .withSchedule(SimpleScheduleBuilder
                            .simpleSchedule()
                            .withIntervalInSeconds(1) //1秒钟执行一次
                            .repeatForever()) //一直执行
                    //设置规则
                    .build();
            //2、创建执行的任务
            JobDetail jobDetail = JobBuilder.newJob(FirstJob.class)
                    //设置job的标识
                    .withIdentity("job1","group1")
                    .usingJobData("name","张三")//设置任务数据
                    .build();
            //3、创建计划
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
            //设置计划,将jobDetail和trigger加入到计划中
            scheduler.scheduleJob(jobDetail,trigger);
            //启动计划
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

4、配置文件

# 名为:quartz.properties,放置在classpath下,如果没有此配置则按默认配置启动
# 指定调度器名称,非实现类
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
# 指定线程池实现类
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# 线程池线程数量
org.quartz.threadPool.threadCount = 10
# 优先级,默认5
org.quartz.threadPool.threadPriority = 5
# 非持久化job,在内存中运行
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

5、核心类说明

Scheduler:调度器。所有的调度都是由它控制Scheduler就是Quartz的大脑,所有任务都是由它来设施 Scheduler包含一个两个重要组件:JobStore和ThreadPool JobStore是会来存储运行时信息的,包括Trigger,Schduler,JobDetail,业务锁等 ThreadPool就是线程池,Quartz有自己的线程池实现。所有任务的都会由线程池执行

SchdulerFactory:顾名思义就是来用创建Schduler了,有两个实现:DirectSchedulerFactory和 StdSchdulerFactory。前者可以用来在代码里定制你自己的Schduler参数。后者是直接读取classpath下的quartz.properties(不存在就都使用默认值)配置来实例化Schduler。通常来讲,我们使用StdSchdulerFactory也就足够了。 SchdulerFactory本身是支持创建RMI stub的,可以用来管理远程的Scheduler,功能与本地一样

三、Trigger

1、SimpleTrigger

以一定的时间间隔(单位是毫秒)执行的任务。

  • 指定起始和截止时间(时间段)
  • 指定时间间隔、执行次数
   Trigger trigger = TriggerBuilder.newTrigger()//此处会先检查加载quartz.properties配置文件,如果没有执行默认规则
                    //设计trigger标识
                    .withIdentity("trigger1","group1")
                    .startNow() //启动立即执行
                    //设置Schedule模型
                    .withSchedule(SimpleScheduleBuilder
                            .simpleSchedule()
                            .withIntervalInSeconds(1) //1秒钟执行一次
                            .repeatForever()) //一直执行
                    .endAt(new GregorianCalendar(2020,7,17,11,49).getTime())
                    .build();
            //1、创建任务调度器,设置执行的时间规则
            Trigger trigger = TriggerBuilder.newTrigger()//此处会先检查加载quartz.properties配置文件,如果没有执行默认规则
                    //设计trigger标识
                    .withIdentity("trigger1","group1")
                    .startNow() //启动立即执行
                    //设置Schedule模型
                    .withSchedule(SimpleScheduleBuilder
                            .simpleSchedule()
                            .withIntervalInSeconds(1) //1秒钟执行一次
                            .withRepeatCount(3)) //执行3次
                    .endAt(new GregorianCalendar(2020,7,17,11,49).getTime())
                    .build();

2、CronTrigger

适合于更复杂的任务,它支持类型于Linux Cron的语法(并且更强大)。

  • 指定Cron表达式即可

示例:
从第3s开始,每3秒运行一次

			//1、创建任务调度器,设置执行的时间规则
            Trigger trigger = TriggerBuilder.newTrigger()//此处会先检查加载quartz.properties配置文件,如果没有执行默认规则
                    //设计trigger标识
                    .withIdentity("trigger1","group1")
                    .startNow() //启动立即执行
                    //设置Schedule模型
                    .withSchedule(CronScheduleBuilder.cronSchedule("3/3 * * * * ?"))
                    .build();

Cron表达式组成
表达式组成:“秒 分 时 日 月 星期几 [年]” ,其中"年" 是可选的,一般不指定。
如:"10 20 18 3 5 ?"代表"5月3日18点20分10秒,星期几不确定 "

位置时间域允许值特殊值
0-59, - * /
分钟0-59, - * /
小时0-23, - * /
日期1-31, - * ? / L W
月份1-12, - * /
星期1-7, - * ? / L #
年份(可选), - * /

Cron表达式符号

表达式中可使用的特殊符号的含义如下

符号语义
星号(*)可用在所有字段中,表示对应时间域的每一个时刻,例如, 在分钟字段时,表示“每分钟”
问号(?)该字符只在日期和星期字段中使用,它通常指定为“不确定值”
减号(-)表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12
逗号(,)表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五
斜杠(/)x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段 中表示5,20,35,50
井号(#)该字符只用在星期字段中,"4#2"代表第二个星期3,“5#4”代表第4个星期四
L该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。
如果L用在星期字段里,则表示星期六,等同于7
L出现在星期字段里,而且在前面有一个数值x,则表示“这个月的最后一个周x”,例如,6L表示该月的最后星期五
L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号
W该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日
例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果
15日是星期日,则匹配16号星期一;如果15号 是星期二,那结果就是15号星期二;但必须
注意关联的匹配日期不能够跨月
LW组合在日期字段可以组合使用LW,它的意思是当月的最后一个工作日

Cron表达式示例

演示实例

表示式说明
0 0 12 * * ?每天12点运行
0 15 10 * * ?每天10:15运行
0 15 10 * * ? 2008在2008年的每天10:15运行
0 * 14 * * ?每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59。
0 0/5 14 * * ?每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55。
0 0/5 14,18 * * ?每天14点到15点每5分钟运行一次,此外每天18点到19点每5钟也运行一次。
0 0-5 14 * * ?每天14:00点到14:05,每分钟运行一次。
0 0-5/2 14 * * ?每天14:00点到14:05,每2分钟运行一次。
0 10,44 14 ? 3 43月每周三的14:10分和14:44分,各运行一次。
0 15 10 ? * 2-6每周一,二,三,四,五的10:15分运行一次。
0 15 10 15 * ?每月15日10:15分运行。
0 15 10 L * ?每月最后一天10:15分运行。
0 15 10 ? * 6L每月最后一个星期五10:15分运行。【此时天必须是"?"】
0 15 10 ? * 6L 2007- 2009在2007,2008,2009年每个月的最后一个星期五的10:15分运行。

四、Spring整合Quartz

1、依赖导入

        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>4.3.22.RELEASE</version>
        </dependency>
        <!--spring事务-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.3.22.RELEASE</version>
        </dependency>

2、定义一个job类

public class FirstJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("任务正在执行中......");
        //获取到执行的任务对象
        JobDetail jobDetail = jobExecutionContext.getJobDetail();
        //获取jobDetail的名称
        String name = jobDetail.getKey().getName();
        String group = jobDetail.getKey().getGroup();
        JobDataMap jobDataMap = jobDetail.getJobDataMap();
        String string = jobDataMap.getString("name");
        System.out.println("正在执行的任务名:"+name+",正在执行任务所在组"+group+",任务数据name的值为:"+string);
    }
}

3、配置

调度器 SchedulerFactoryBean
触发器 CronTriggerFactoryBean
JobDetail JobDetailFactoryBean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--
    spring整合quartz进行配置遵循如下步骤:
    1、定义工作的job
    2、定义触发器Trigger,并将触发器与工作任务绑定
    3、定义调度器,并将Trigger注册到Scheduler
    -->

    <!--配置执行的任务-->
    <bean id="jobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <!--指定job名称-->
        <property name="name" value="job1"/>
        <!--指定job分组-->
        <property name="group" value="group1"/>
        <!--指定job的具体类-->
        <property name="jobClass" value="com.booy.quartzdemo.SpringJob"/>
        <!--  指定jobDataAsMap  -->
        <property name="jobDataAsMap">
            <map>
                <entry key="name" value="张三"/>
            </map>
        </property>
    </bean>

    <!--配置任务调度器Trigger-->
    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <!--指定Trigger名称-->
        <property name="name" value="trigger"/>
        <!--指定Trigger组名 -->
        <property name="group" value="group1"/>
        <!--指定Trigger的定时任务cron表达式,从3s开始每5s运行一次-->
        <property name="cronExpression" value="3/5 * * * * ?"/>
        <!--指定Trigger的jobDetail-->
        <property name="jobDetail" ref="jobDetail"/>
    </bean>

    <!--配置计划,将Trigger注册进scheduler-->
    <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <!--配置调度器-->
        <property name="triggers" ref="cronTrigger"/>
        <!--可配置多个cronTrigger-->
        <!--<property name="triggers">
                    <list>
                        <ref bean="cronTrigger"/>
                    </list>
                </property>-->
        <!--添加 quartz 配置,如下两种方式均可-->
        <!--<property name="configLocation" value="classpath:quartz.properties"> </property>-->
<!--        <property name="quartzProperties">
            <value>
                配置信息
            </value>
        </property>-->
    </bean>
</beans>

4、操作

启动任务

工厂启动,调度器启动,任务调度开始

public class MyMain {
    public static void main(String[] args) {
        // 工厂启动,任务启动,工厂关闭,任务停止
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    }
}

任务操作

删除任务

public class MyMain {
    public static void main(String[] args) {
        // 工厂启动,任务启动,工厂关闭,任务停止
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Scheduler scheduler = (Scheduler)applicationContext.getBean("scheduler");
        System.out.println(scheduler.getClass());
        try {
            Thread.sleep(10000);
            scheduler.deleteJob(JobKey.jobKey("job1","group1"));//删除任务
        } catch (InterruptedException | SchedulerException e) {
            e.printStackTrace();
        }
    }
}

暂停、恢复

public class MyMain {
    public static void main(String[] args) {
        // 工厂启动,任务启动,工厂关闭,任务停止
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Scheduler scheduler = (Scheduler)applicationContext.getBean("scheduler");
        try {
            Thread.sleep(10000);
            scheduler.pauseJob(JobKey.jobKey("job1","group1"));//暂停工作
            Thread.sleep(3000);
            scheduler.resumeJob(JobKey.jobKey("job1","group1"));//恢复工作
        } catch (InterruptedException | SchedulerException e) {
            e.printStackTrace();
        }
    }
}

批量操作

public class MyMain {
    public static void main(String[] args) {
        // 工厂启动,任务启动,工厂关闭,任务停止
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        Scheduler scheduler = (Scheduler)applicationContext.getBean("scheduler");
        try {
            Thread.sleep(10000);
            GroupMatcher<JobKey> group1 = GroupMatcher.groupEquals("group1");
            scheduler.pauseJobs(group1); //暂停组中所有工作
            Thread.sleep(10000);
            scheduler.resumeJobs(group1); //恢复组中所有工作
        } catch (InterruptedException | SchedulerException e) {
            e.printStackTrace();
        }
    }
}

五、 其他任务调度方式

1、使用Timer创建简单的定时任务

public class TimerDemo {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("TimerTask run:"+ LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
            }
        },1000,3000);//延时1s,之后每隔3s运行一次
    }
}

注意:
1)、scheduleAtFixedRate 和 schedule 的区别: scheduleAtFixedRate 会尽量减少漏掉调度的情况,如果前一次执行时间过长,导致一个或几个任务漏掉了,那么会补回来,而schedule 过去的不会补,直接加上间隔时间执行下一次任务。
2)、同一个 Timer 下添加多个 TimerTask ,如果其中一个没有捕获抛出的异常,则全部任务都会终止运行。但是多个 Timer 是互不影响

2、在springboot环境下使用@Scheduled创建定时任务

步骤:
1)、启动类添加 @EnableScheduling
2)、定时任务方法上添加 @Scheduled

@Component
public class springScheduledDemo {
    @Scheduled(cron = "1/5 * * * * ?")
    public void testScheduled() {
        System.out.println("springScheduled run:" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss")));
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值