Quartz

本文介绍了Quartz作为定时任务框架的使用,包括新建Maven项目,添加依赖,创建Job,定义Trigger,配置线程池,以及使用CronTrigger设定复杂时间规则。还讨论了任务的控制,如删除、暂停和恢复任务,并涉及SpringBoot的相关知识。
摘要由CSDN通过智能技术生成

1、认识定时任务框架

场景:1、活动的开始

​ 2、定时更新数据

在这里插入图片描述
​ 3、新闻的推送等等

概念:在指定的时间,去调用、执行特定的任务。

2、通过简单案例使用Quartz定时任务框架

2.1 新建maven项目

2.2 添加jar依赖

<packaging>war</packaging>
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!--Quartz任务调度-->
    <!-- https://mvnrepository.com/artifact/org.quartz-scheduler/quartz -->
    <dependency>
        <groupId>org.quartz-scheduler</groupId>
        <artifactId>quartz</artifactId>
        <version>2.2.3</version>
    </dependency>
</dependencies>

3、定时任务的体系结构

1、新建任务 Job:要执行的业务代码

2、声明触发器 Trigger: 定义时间规则

3、声明调度器Scheduler: 把任务和触发器 关联在一起,开启即可

4、新建一个Job

package com.qf.quartz2205.jobs;

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

public class FirstJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        //编写指定的时间,具体要执行的任务
        
        //获取当前任务的详细信息
        JobDetail jobDetail=context.getJobDetail();
        
        //开启活动
        //更新缓存
        //推送新闻等等
        for(int i=1; i<=5; i++){
            //jobDetail.getKey().getName() 获取的是当前任务的名字
            //jobDetail.getKey().getGroup() 获取的是任务所在组的名字
            System.out.println("任务名:"+jobDetail.getKey().getName()+
                    ",任务所在组"+jobDetail.getKey().getGroup()+
                    "执行了i="+i);
        }
    }
}

5、在main方法中,定义时间规则及触发器

package com.qf.quartz2205.test;

import com.qf.quartz2205.jobs.FirstJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

import java.util.GregorianCalendar;

public class Test1 {
    public static void main(String[] args) {
        //测试使用定时任务

        //1、定义触发器:定义时间规则
        Trigger trigger= TriggerBuilder.newTrigger()
                .withIdentity("firstTrigger","triggerGroup1") // 参数1:触发器的名字,参数2:触发器所在的组名
                .startNow() //触发器立刻开始生效,startAt()  指定Date类型的时间,在指定的时间生效
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                    .withIntervalInSeconds(3)  //每隔3秒执行一次
                    .repeatForever())  //一直重复   也可以通过repeatCount(20) 指定重复执行的次数
                .endAt(new GregorianCalendar(2023,4,17,11,20,0).getTime())
                //endAt   结束的时间
                .build();


        try {
            //2、定义调度器
            Scheduler scheduler= StdSchedulerFactory.getDefaultScheduler();

            //3、把任务封装在JobDetail类中
            JobDetail jobDetail=JobBuilder.newJob(FirstJob.class)
                    .withIdentity("job1","jobGroup1")//  参数1:任务的名字  参数2:任务所在组的名
                    .build();

            //通过调度器scheduler把trigger  和  jobdetail封装、关联在一起
            scheduler.scheduleJob(jobDetail,trigger);

            //开启
            scheduler.start();

        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

小结:

1、编写任务 实现Job接口

2、编写时间规则: Trigger

3、编写调度器: Scheduler

4、【说明】每一个任务都是一个独立的线程,每个定时任务,有自己的时间比较器

6、定时任务的线程池配置-了解

场景:当一个服务中的定时任务比较多的时候,为了高效使用定时任务,需要开启线程池。

【说明】线程池的好处:

​ 1、节省自行创建、销毁线程的时间

​ 2、线程池防止我们无限制的创建线程资源,导致程序崩溃

6.1 在项目的resources路径下,创建quartz.properties文件

6.2 在quartz.properties编写线程池的配置信息

# 线程池里的线程由谁来创建
org.quartz.scheduler.instanceName = DefaultQuartzScheduler
# 线程池的类别
org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool
# 线程池中线程的数量
org.quartz.threadPool.threadCount = 10
# 设置线程池中线程的优先级
org.quartz.threadPool.threadPriority = 5
# 线程执行的任务,无需持久化
org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore

线程池的使用无需编写任何代码,直接使用即可


【面试题1】1、为什么使用springboot 而不使用spring spring mvc?

​ 1、重约束、轻配置

​ 2、内置了大量的组件,简化开发 例如tomcat

​ 3、springboot的starter机制,节省了jar的依赖

【面试题2】如何理解springboot的重约束、轻配置?

​ 1、springboot的启动类,放在父包中,无需配置扫描的基本包

​ 2、配置文件的名字 application 自动读取

【面试题】springboot 如何使用外部的tomcat?

【面试题】springboot的starter是什么?如何自己开发一个starter


7、Trigger-重点

Trigger的作用:定义时间规则,在什么时候去执行任务。

分两种类型:
Trigger类型1:SimpleTrigger-了解

只能定义简单的时间规则:间隔多长时间,以及重复次数等

Trigger类型2:CronTrigger-重点

能够定义复杂的时间规则,通过Cron表达式来定义时间规则

一、组成规则:由6或7位组成

秒 分 时 日期 月份 星期 [年份]

【说明】日期、星期中,只要设置其中之一即可

二、符号组成

在这里插入图片描述
符号含义

, 表示列表,用于列表值的分隔 2,4,7,8,9 * * * * *

- 表示一个连续的范围 2,4,7-9 * * * * *

* 表示每一个,看具体出现的位置

/ 表示步长 递增多少 3/5 * * * * * 从第3秒开始,每过5秒执行定时任务

? 表示任意值,只能出现在日期 星期位置中,当设置了日期 星期中的任意一个,那么另一个就可以使用?

# 只能用在星期中, 星期几#第几个 案例 4#2 第个星期3 1-星期天 2-星期一 3-星期二 4-星期三

L 出现在日期或星期中,通常表示最后一个 10 20 5 ? 4 L 4月份每个星期的星期六的5时20分10秒的时候

​ 10 20 5 ? 4 3L 4月的最后一个星期2的5时20分10秒

​ 出现在星期的位置:如果单独出现,表示每个星期的最后一天,也就是星期六

​ 如果XL ,表示当月的最后一个星期X-1

​ 出现在日期的位置:表示当月的最后一天(自动计算大、小月份、平润年)

W 只能出现在日期中 XW 表示离X最近的一个工作日 22W 指的是21号 21W

​ 注意不跨月

LW 出现在日期中,表示当月的最后一个工作日

符号语义
星号(*)可用在所有字段中,表示对应时间域的每一个时刻,例如, 在分钟字段时,表示“每分钟”
问号(?)该字符只在日期和星期字段中使用,它通常指定为“不确定值”
减号(-)表达一个范围,如在小时字段中使用“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,它的意思是当月的最后一个工作日

练习:

表示式说明
0 0 12 * * ?每天的12:00:00
0 15 10 * * ?每天的10:15:00
0 15 10 * * ? 20082008年的每天的10:15:00
0 * 14 * * ?每天14时每一分的0秒开始执行一次
0 0/5 14 * * ?每天14时从0分开始每隔5分中的第0秒开始执行
0 0/5 14,18 * * ?每天打的14时、18时的从0分开始每隔5分中的0秒开始执行
0 0-5 14 * * ?每天的14时的0、1、2、3、4、5分的0秒开始执行
0 0-5/2 14 * * ?每天14时0分、2分、4分的0秒开始执行
0 10,44 14 ? 3 43月份的每周三的14时的10分、44分 0秒时执行
0 15 10 ? * 2-6每个工作日的10:15:00执行
0 15 10 15 * ?每个月的15号的10:15:00
0 15 10 L * ?每月最后一天的10:15:00
0 15 10 ? * 6L每月的最后一个星期五的10:15:00
0 15 10 ? * 6L 2007-20092007、2008、2009三年中每月的最后一个星期五的10:15:00

8、使用CronTriggeer

package com.qf.quartz2205.test;

import com.qf.quartz2205.jobs.FirstJob;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class Test2 {
    public static void main(String[] args) {
        //使用CronTrigger
        //1、定义CronTrigger
        Trigger trigger= TriggerBuilder.newTrigger()
                .withIdentity("cronTrigger","cronGroup1")
                .withSchedule(CronScheduleBuilder.cronSchedule("0,5,13 * * 17 * ?"))
                .build();

        try {
            //定义调度器
            Scheduler scheduler= StdSchedulerFactory.getDefaultScheduler();

            //把任务封装在JobDetail中
            JobDetail jobDetail=JobBuilder.newJob(FirstJob.class)
                    .withIdentity("job2","jobGroup2")
                    .build();

            //关联触发器和任务
            scheduler.scheduleJob(jobDetail,trigger);
            scheduler.start();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }
}

9、任务的控制

package com.qf.quartz2205.service.impl;

import com.qf.quartz2205.jobs.FirstJob;
import com.qf.quartz2205.service.LoopService;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.springframework.stereotype.Service;

@Service
public class LoopServiceImpl implements LoopService {
    //声明调度属性
    private Scheduler scheduler;
    @Override
    public String startJob(String name, String group) {
        String result="";
        try {
            //创建调度对象
            scheduler= StdSchedulerFactory.getDefaultScheduler();
            //创建CronTrigger
            Trigger cronTrigger= TriggerBuilder.newTrigger()
                    .withIdentity("cronTrigger","griggerGroup")
                    .withSchedule(CronScheduleBuilder.cronSchedule("5,10,15 * * 18 4 ?"))
                    .build();
            JobDetail jobDetail=JobBuilder.newJob(FirstJob.class)
                    .withIdentity(name,group)
                    .build();

            scheduler.scheduleJob(jobDetail,cronTrigger);
            scheduler.start();
            result="start success";
        } catch (SchedulerException e) {
            result="start failed";
            e.printStackTrace();
        }
        return result;
    }

    @Override
    public String deleteJob(String name, String group) {
        String result="";
        //删除任务之前一定要开启任务
        if(this.scheduler!=null){
            //把任务的名字,组名封装在JobKey对象中
            JobKey jobKey=JobKey.jobKey(name,group);
            try {
                scheduler.deleteJob(jobKey);
                result="delete success";
            } catch (SchedulerException e) {
                result="delete failed";
                e.printStackTrace();
            }
        }
        return result;
    }
}

9.1 删除任务

@Override
public String deleteJob(String name, String group) {
    String result="";
    //删除任务之前一定要开启任务
    if(this.scheduler!=null){
        //把任务的名字,组名封装在JobKey对象中
        JobKey jobKey=JobKey.jobKey(name,group);
        try {
            scheduler.deleteJob(jobKey);
            result="delete success";
        } catch (SchedulerException e) {
            result="delete failed";
            e.printStackTrace();
        }
    }
    return result;
}

3.2 暂停、恢复任务

@Override
public String pauseJob(String name, String group) {
    String result="";
    //把需要暂停的任务封装在JobKey中
    JobKey jobKey=JobKey.jobKey(name,group);

    //执行暂停
    try {
        scheduler.pauseJob(jobKey);
        result="pause success";
    } catch (SchedulerException e) {
        result="pause failed";
        e.printStackTrace();
    }
    return result;
}

    @Override
    public String resumeJob(String name, String group) {
        String result="";

        JobKey jobKey=JobKey.jobKey(name, group);
        try {
            scheduler.resumeJob(jobKey);
            result="resume success";
        } catch (SchedulerException e) {
            result="resume failed";
            e.printStackTrace();
        }
        return result;
    }

3.3 任务的批量处理

可以一次性处理多个任务,暂停、恢复多个

【准备】又开启了一个任务,当前任务和之前的任务必须使用同一个Scheduler,否则批量操作失效

@Override
public String startJob2(String name, String group) {
    String result="";
    if(this.scheduler==null){
        try {
            scheduler=StdSchedulerFactory.getDefaultScheduler();
        } catch (SchedulerException e) {
            e.printStackTrace();
        }
    }else{
        //定义时间规则
        Trigger trigger=TriggerBuilder.newTrigger()
                .withIdentity("trigger2","griggerGroup2")
                .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
                .build();
        JobDetail jobDetail=JobBuilder.newJob(SecondJob.class)
                .withIdentity(name,group)
                .build();

        try {
            scheduler.scheduleJob(jobDetail,trigger);
            result="start job2 success";
        } catch (SchedulerException e) {
            result="start job2 failed";
            e.printStackTrace();
        }
    }
    return result;
}

批量暂停、恢复

@Override
public String pauseJobs(String group) {

    //把组名直接封装在GroupMatcher中
    GroupMatcher<JobKey> groupMatcher=GroupMatcher.groupEquals(group);
    String result="";
    try {
        scheduler.pauseJobs(groupMatcher);
        result="批量暂停成功";
    } catch (SchedulerException e) {
        result="批量暂停失败";
        e.printStackTrace();
    }
    return null;
}

@Override
public String resumeJobs(String group) {
    GroupMatcher<JobKey> groupMatcher=GroupMatcher.groupEquals(group);
    String result="";
    try {
        scheduler.resumeJobs(groupMatcher);
        result="批量恢复成功";
    } catch (SchedulerException e) {
        result="批量恢复失败";
        e.printStackTrace();
    }
    return result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值