01.Quartz

Quartz

1.什么时候需要任务调度

  1. 信用卡账单日还款日通知
  2. 银行核心系统跑批
  3. 人行二代支付系统半个小时打一次包

2.任务调度的组成

  1. 触发调度的规则
  2. 调度后执行的行为:代码,任务,脚本
  3. 集中管理配置
  4. 并发执行,互不干扰
  5. 调度器,管理任务
  6. 集成Spring

3.任务调度工具

  1. Cron Tab
  2. Timer
  3. ScheduledThreadPoolExecutor
  4. Quartz
  5. SpringTask
  6. Elastic-Job
  7. XXL-JOB

4.定时任务的基础知识

  1. 小顶堆
  2. 时间轮算法

5.小顶堆

什么是堆

堆是—种特殊的树,只要满足下面两个条件,它就是一个堆:

  1. 堆是一颗完全二叉树
  2. 堆中某个节点的值总是不大于(或不小于)其父节点的值
  3. 最后一层靠左排列

什么是完全二叉树

在二叉树中,除了最后一层,每一层都是满的才可以称之为完全二叉树

数组与完全二叉树

完全二叉树最适合用数组做存储,因为它的节点都是紧凑的,且只有最后一层节点数不满

为什么下标0的位置不存在元素呢?

这是因为这样存储我们可以很方便地找到父节点:下标/2,比如,4的父节点即4/2=2,5的父节点即5/2=2。

小顶堆定义

堆也是一颗完全二叉树,但是它的元素必须满足每个节点的值都不大于(或不小于(小顶堆))其父节点的值。比如下面这个堆就是一个小顶堆:

小顶堆的存储实现

这种存储能够根据下标位置很快找到父节点

这时候我们要找8的父节点就拿8的位置下标5/2=2,也就是5这个节点的位置,这也是为了我们后面堆化

小顶堆插入元素

插入尾部,然后上浮

小顶堆删除元素

将尾部(最大的元素)元素放到堆顶,然后下沉

6.时间轮算法

为什么拥有了小顶堆还需要时间轮

小顶堆虽然也能实现定时调度,但是定时任务对于删除操作是比较频繁的,在小顶堆结构中需要频繁的下沉操作会很浪费性能

时间轮的分类

  1. while-true-sleep时间轮
  2. round型时间轮
  3. 分层时间轮

7.Timer

什么是Timer

是JDK提供的定时调度的一个类

使用Timer

public class Test {
  public static void main(String[] args){
    // new的时候任务就启动了
    Timer t = new Timer();
    for(int i=0;i<2;i++){
      TimerTask task = new FooTimerTask("foo"+i);
      // 任务提交
      t.schedule(task,new Date(),2000);
    }
  }
}
class FooTimerTask extends TimerTask {
  private String name;

  public FooTimerTask(String name){
    this.name=name;
  }

  public void run(){
    try{
      Syste.out.println("name="+name);
      Thread.sleep(3000);
    }catch(Exception e){
    }
  }
}

Timer的问题

  1. 单线程执行任务,任务可能互相阻塞
  2. 运行时异常会导致Timer线程终止
  3. 任务调度是基于绝对时间的,对系统时间敏感,无法做到类似于每个月的10天去做什么这种调度

8.ScheduledThreadPoolExecutor

相比于Timer的优点

  1. 使用多线程执行任务,不会相互阻塞
  2. 如果线程失活,会新建线程执行任务
  3. 也是用小顶堆实现的

Leader-Follower模式

假如说现在有一堆等待执行的任务(一般是存放在一个队列中排好序),而所有的工作线程中只会有一个是leader线程,其他的线程都是follower线程。只有leader线程能执行任务,而剩下的follower线程则不会执行任务,它们会处在休眠中的状态。当leader线程拿到任务后执行任务前,自己会变成follower线程,同时会选出一个新的leader线程,然后才去执行任务。如果此时有下一个任务,就是这个新的leader线程来执行了,并以此往复这个过程。当之前那个执行任务的线程执行完毕再回来时,会判断如果此时已经没任务了,又或者有任务但是有其他的线程作为leader线程,那么自己就休眠了;如果此时有任务但是没有leader线程,那么自己就会重新成为leader线程来执行任务

避免没必要的唤醒和阻塞的操作,这样会更加有效,且节省资源。

9.Quartz

Quartz介绍

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与2EE与)2SE应用程序相结合也可以单独使用

Quartz是一个特性丰富的,开源的任务调度库,它几乎可以嵌入所有的Java程序,从很小的独立应用程序到大型商业系统。Quartz可以用来创建成百上千的简单的或者复杂的任务,这些任务可以用来执行任何程序可以做的事情。Quartz拥有很多企业级的特性,包括支持JTA事务和集群

Quartz运行环境

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

Quartz设计模式

  1. Builder设计模式
  2. 工厂模式
  3. 组件模式
  4. 链式编程

Quartz核心概念

任务(Job)

Job就是你想要实现的任务类,每一个ob必须实现org.quartz.job接口,且只需实现接口定义的execute()方法

触发器(Trigger)

Trigger为你执行任务的触发器,比如你想每天定时3点发送一份统计邮件,Trigger将会设置3点进行执行该任务。Trigger主要包含两种SimpleTrigger和CronTrigger两种

调度器(Scheduler)

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

Quartz的体系结构

Quartz的几个常用API

  1. Scheduler用于与调度程序交互的主程序接口,Scheduler调度程序-任务执行计划表,只有安排进执行计划的任务Job(通过scheduler.scheduleJob方法安排进执行计划),当它预先定义的执行时间到了的时候(任务触发Trigger),该任务才会执行
  2. Job我们预先定义的希望在未来时间能被调度程序执行的任务类,我们可以自定义
  3. JobDetail使用JobDetail来定义定时任务的实例,JobDetail实例是通过JobBuilder类创建的
  4. JobDataMap可以包含不限量的(序列化)数据对象,在Job实例执行的时候,可以使用其中的数据。JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数据的方法
  5. Trigger触发器,Trigger对象用来触发执行Job的。当调度一个Job时,我们实例一个触发器然后调整它的属性来满足Job执行的条件。表明任务在什么时候会执行。定义了一个已经被安排的任务将会在什么时候执行的时间条件,比如2秒就执行一次
  6. JobBuilder:用于声明一个任务实例,也可以定义该任务的详情比如任务名,组名等,这个声明的实例将会作为一个实际执行的任务
  7. TriggerBuilder:触发器创建器,用于创建触发器Trigger实例
  8. JobListener,TriggerListener,SchedulerListener监听器,用于对组件的监听

Quartz基本使用

依赖

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

实现Job接口

/**
 * @author 单国玉
 * @date 2022/8/17 12:35
 * @description
 */
public class HelloJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("当前开始时间:"+LocalDateTime.now().toString());
        System.out.println("当前结束时间:"+LocalDateTime.now().toString());

    }
}

测试运行


/**
 * @author 单国玉
 * @date 2022/8/17 12:34
 * @description
 */
public class TestQuartz {
    public static void main(String[] args) throws Exception{
        // 1.调度器(Scheduler)
        Scheduler defaultScheduler = StdSchedulerFactory.getDefaultScheduler();
        // 2.任务实例(JobDetail)
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
                .withIdentity("job1", "group1") // 任务名称与任务组的名称
                .build();
        // 3.触发器(Trigger)
        SimpleTrigger simpleTrigger = TriggerBuilder.newTrigger()
                .withIdentity("trigger1", "group1") // 触发器名称与组名称
                .startNow() // 马上启动触发器
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
                .build();
        // 4.关联Job与Trigger
        defaultScheduler.scheduleJob(jobDetail, simpleTrigger);
        // 5.启动
        defaultScheduler.start();
    }
}

Job和JobDetail介绍

  1. Job: 工作任务调度的接口,任务类需要实现该接口。该接口中定义execute方法,类似JDK提供的TimerTask类的run方法。在里面编写任务执行的业务逻辑
  2. Job实例在Quartz中的生命周期: 每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例,当调用完成后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收
  3. JobDetail: JobDetail为Job实例提供了许多设置属性,以及JobDetaMap成员变量属性,它用来存储特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例
  4. JobDetail重要属性: name,group,jobClass,jobDataMap

JobExecutionContext介绍

  1. 当Scheduler调用一个Job,就会将JobExecutionContext传递给Job的execute方法
  2. Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据

JobDataMap介绍

  1. 使用Map获取,在进行任务调度时候JobDataMap存储在JobExecutionContext中非常方便获取
  2. JobDataMap可以用来装载任何可序列化的数据对象,当Job实例对象被执行时这些参数对象会传递给它
  3. JobDataMap实现了JDK的Map接口,并且添加了非常方便的方法用来存取基本数据类型
posted @   单国玉  阅读(48)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示