JAVA - Quartz 定时任务_启动原理

article/2025/9/24 1:25:57

JAVA - Quartz 定时任务_启动原理

前言

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

简单的定时任务实现可以借助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原理

本篇博客接着上两篇

  • Java - Quarz 定时任务
  • Java - Quarz 定时任务_使用注意点

这篇主要说一下 Quartz 的执行原理,在JobDetail 与 Trigger 执行好了之后,都是 通过 Scheduler 来完成任务的执行

前言

1、数据存储

Quartz 对于 trigger 和 job 的存储下来主要有两种存储方式:

  • RAMJobStore:将 trigger 和 job 存储在内存中,存取速度快,但是系统被停止后数据会丢失
  • JobStoreSupport:基于 jdbc 将 trigger 和 job 存储到数据库中,存储速度慢一些但保证数据不丢失

在 Quarz 的默认配置文件中,存储默认为 RAMJobStore,若想更改存储方式,可以新增相应的 JobStore 配置与数据库配置

example:

#==============================================================
#Configure JobStore
#==============================================================*
# 指定 class  JobStoreTX(自己管理事务) /  JobStoreCMT(依赖于容器来进行事务的管理)
org.quartz.jobStore.class = org.quartz.impl.jdbcjobstore.JobStoreTX 
# 驱动代理
org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.StdJDBCDelegate 
# 数据库表的前缀
org.quartz.jobStore.tablePrefix = QRTZ_ 
# 是否集群
org.quartz.jobStore.isClustered = true
org.quartz.jobStore.clusterCheckinInterval = 20000 
org.quartz.jobStore.dataSource = myDS 
org.quartz.jobStore.maxMisfiresToHandleAtATime = 1 
org.quartz.jobStore.misfireThreshold = 120000 
org.quartz.jobStore.txIsolationLevelSerializable = false 
#============================================================== 
#Configure DataSource
#============================================================== 
org.quartz.dataSource.myDS.driver = com.mysql.jdbc.Driver 
org.quartz.dataSource.myDS.URL = 你的数据链接 
org.quartz.dataSource.myDS.user = 用户名 
org.quartz.dataSource.myDS.password = 密码 
org.quartz.dataSource.myDS.maxConnections = 30 
org.quartz.jobStore.selectWithLockSQL = SELECT * FROM {0}LOCKS WHERE LOCK_NAME = ? FOR UPDATE

说明:上述配置中,JobStoreSupport 使用了一个驱动代理 StdJDBCDelegate 来操作 trigger 和 job 的数据存储。其实现了大部分基于标准 JDBC 的功能接口,但是对于各种数据库来说,需要根据其具体实现的特点做某些特殊处理,所以 StdJDBCDelegate 有很多实现类来完成这些特殊处理,根据需求也可以使用任意一个驱动代理

请添加图片描述

2、线程

Quartz 中,有两类线程:

  • 调度线程
    • 执行 常规调度 的线程 QuartzSchedulerThread:轮询存储的所有 trigger,如果有需要触发的 trigger,即到达了下一次触发的时间,则从任务执行线程池获取一个空闲线程,执行与该 trigger 关联的任务
    • 执行 misfired trigger 的线程 MisfireHandler:扫描所有的 trigger,查看是否有 misfired trigger,如果有的话根据 misfire 的策略分别处理
  • 任务执行线程:通常使用一个线程池维护一组线程,默认是使用 SimpleThreadPool 线程池

misfired job 是因为一些原因产生了 misfired 任务,如下:

  • 系统被重启,有些任务在系统在关闭到重新启动中的时间内可能会被 misfire;
  • 有些任务在 Trigger 被暂停的一段时间里可能会被 misfire;
  • 线程池中线程都被占用,任务无法再被触发执行,造成 misfire;
  • 有状态任务在下次触发时间到达时,上次执行还没有结束;为了处理 misfired job,Quartz 中为 trigger 定义了处理策略,主要有下面两种:
    • MISFIRE_INSTRUCTION_FIRE_ONCE_NOW:针对 misfired job 马上执行一次;
    • MISFIRE_INSTRUCTION_DO_NOTHING:忽略 misfired job,等待下次触发;默认是

原理与流程

当 Quartz 与 Spring 应用集成时,服务器启动会装载相关的 Bean,即 SchedulerFactoryBean,其实现了 InitializingBean 接口,在初始化Bean时会执行afterPropertiesSet 方法,该方法将会创建 Scheduler

Scheduler 两个具体的创建工厂类:DirectSchedulerFactoryStdSchedulerFactory

  • StdSchedulerFactoryDirectSchedulerFactory 创建出的Scheduler (createScheduler方法 )的类型都是 org.quartz.impl.StdScheduler
  • DirectSchedulerFactory 创建出的Scheduler (createRemoteScheduler方法)的类型是 org.quartz.impl.RemoteScheduler

一般情况下,通常用 StdSchedulerFactory 来创建 Bean(下面的原理以 StdSchedulerFactory 创建 Scheduler 为 讲解 )

SchedulerFactory 在创建 Scheduler 的过程中,会读取配置文件的相关配置信息,从而来初始化各个组件,若不自定义,默认的为其自带的 quartz.properties 文件,相关默认配置如下:

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#
org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false# 线程池的默认配置,配置前缀为 org.quartz.threadPool
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: trueorg.quartz.jobStore.misfireThreshold: 60000org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

说明:(若不采用上述默认的配置,建立 org.quartz.properties 文件自定义相关key即可)

  • 默认instanceName 为 DefaultQuartzScheduler
  • 默认的线程池采用 SimpleThreadPool 线程池,线程个数为 10 ,优先级为5
  • 默认的 jobStore 存储为 RAMJobStore

创建 Scheduler 的过程中根据配置文件的相关配置,若有相关配置会初始化一些组件,无相关配置则以默认的配置进行初始化,一些组件如下:

  • org.quartz.impl.StdSchedulerFactory#instantiate
    • ThreadPool:根据配置文件中 org.quartz.threadPool.class 进行配置,线程池内部配置获取以 org.quartz.threadPool 为前缀的配置进行初始化, 默认使用 SimpleThreadPool(org.quartz.simpl.SimpleThreadPool)其创建了一定数量的 WorkerThread 实例来使得 Job 能够在线程中进行处理,其中有三个list:workers-存放池中所有的线程引用,availWorkers-存放所有空闲的线程,busyWorkers-存放所有工作中的线程;
    • JobStore:分别为 RAMJobStore 和 JobStoreSupport ,默认使用 RAMJobStore (内存存储),可通过配置文件中 org.quartz.jobStore.class 来实现自定义
    • QuartzSchedulerThread:创建出的 QuartzScheduler 中的 schedThread ,用来进行任务调度,在初始化的时候 paused=true, halted=false,这个线程已经准备就绪,因其paused=true,所以线程会一直等待,直到 start 方法将paused置为false;

整个启动流程如下图(默认配置):

请添加图片描述

说明:

  • 先读取配置文件,读取系统的 org.quartz.properties 文件,若没有,读取自带的 quartz.properties 文件,采用默认配置来进行初始化
  • 初始化 SchedulerFactoryBean,InitializingBean 方法中 初始化 SchedulerFactory
  • 调用 **getScheduler **方法触发 StdSchedulerFactory 的 instantiate 方法进行实例化
  • 实例化 执行线程池,默认线程池为 SimpleThreadPool ,可通过配置文件中 org.quartz.threadPool.class 自定义
  • 实例化数据存储,默认为 RAMJobStore (内存存储),可通过配置文件中 org.quartz.jobStore.class 自定义
  • 初始化QuartzScheduler,new一个QuartzSchedulerThread调度线程,并开始运行,初始字段 paused=true, halted=false
  • 调度开始,注册监听器,注册Job和Trigger
  • 调用 start() 方法,改变 paused=false,执行调度线程

总结

Scheduler 具体常用的一些方法:

方法名称描述
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删除定时任务

上述说到 org.quartz.impl.StdScheduler 里面调用的都是 org.quartz.core.QuartzScheduler 的方法,所以核心的业务逻辑与原理都在该类中实现,包括上面的常用方法,均是通过调用 org.quartz.core.QuartzScheduler 的对应方法来实现

总览:

请添加图片描述

说明:

  • 一些方法对应上述表格中的各个方法的实现,实现逻辑与原理相似
  • 通过调用关联的 JobStore(即 RAMJobStore 和 JobStoreSupport ) 里面的方法来具体实现

关于后续的流程还是有待继续探讨的~


http://chatgpt.dhexx.cn/article/p1Fwi5aX.shtml

相关文章

如何应用quartz定时任务?

Quartz可以用来做什么&#xff1f; Quartz是一个强大任务调度框架&#xff0c;我工作时候会在这些情况下使用到quartz框架&#xff0c;当然还有很多的应用场景&#xff0c;在这里只列举2个实际用到的 餐厅系统会在每周四晚上的22点自动审核并生成报表人事系统会在每天早晨8点…

Quartz定时任务基础学习

Quartz基础笔记 前言 1、什么是Quartz Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目。 2、Quartz的运行环境是什么&#xff1f; Quartz可以运行嵌入在一个独立式应用程序Quartz可以在应用服务器或者Servlet容器实例化&#xff0c;并且参与事务Quartz可…

Quartz定时任务

Java实现定时任务的方式 一、线程等待&#xff08;不建议使用&#xff0c;任务复杂时存在内存泄露风险&#xff09; Thread myThread new Thread(new Runnable() {Overridepublic void run() {while (true) {System.out.println("TestThreadWait is called!");try…

java定时任务Quartz整理

目录 一、Quartz介绍 二、Quartz的组成 三、使用java实现一个简单的Quartz例子 四、使用Springboot整合Quartz定时任务框架 五、使用Springbootmybatis整合Quartz定时任务框架实现定时向数据库插入一条数据 六、总结 七、参考资料 一、Quartz介绍 quartz是一种基于java…

Quartz之定时任务

一 基础概念 1.什么是Quartz? Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目&#xff0c;完全由Java开发&#xff0c;可以用来执行定时任务&#xff0c;类似于 java.util.Timer。但是相较于Timer&#xff0c; Quartz增加了很多功能&#xff1a;(1).持久性…

定时任务框架Quartz

目录 一、Quartzh介绍 1、Quartz的定义&#xff1a; 2、Quartz完成定时任务的原理&#xff1a; 二、cron表达式 1、cron表达式定义 2、cron表达式规范 三、spring自带调度器 四、Quartz内存版集成基本使用 1、关于Quartz内存版集成的步骤 1、先到启动类中写好集成的步骤 2、…

定时任务:Quartz 详解

定时任务&#xff1a;Quartz 详解 文章目录 定时任务&#xff1a;Quartz 详解1 Quartz是什么&#xff1f;2 Quartz核心组成3 Quartz核心模块理解3.1 用工厂模式理解 Quartz 的设计机制&#xff1a;3.2 用流程图理解 Quartz 的核心模块关系&#xff1a; 4 Quartz详解4.1 Quartz的…

Linux 环境下的 for循环嵌套学习

题目&#xff1a;输出下面的 4*5 的矩阵&#xff1a; 1 2 3 4 5 2 4 6 8 10 3 6 9 12 15 4 8 12 16 20 解: 首先打开Xfce终端&#xff0c;创建c文件&#xff0c; 并用gedit记事本打开它。 touch zhanglong.c gedit zhanglong.c之后输入代码&#xff1a; #include<std…

for循环嵌套编程练习

1 编程要求:求出用50元,20元和10元换算100元有几种方式? 思路:用穷举法,将所有可能的情况都列举出来,用for循环可以实现穷举 分析:100元单用50换算,最多需要两张,用20元最多换算5张,用10元最多换算10张 #include<stdio.h> int main(void) {int w,e,s; // w代表…

c语言99乘法表循环嵌套写法,99乘法表(for循环嵌套)

计算乘法表 两个数相乘,外层循环代表被乘数,控制行数;内层代表乘数,控制列数。 循环嵌套,变量名不可以重复。 使用 break 语句让输出的乘法表更简洁。 使用 String 变量,做 String 的加法。 public class MultiTabble {public static void main(String[] args) {for (int…

关于For循环嵌套的简单理解

一&#xff1a;循环语句的嵌套 一个循环结构内可以含有另一个循环&#xff0c;称为循环嵌套&#xff0c;又称多重循环。常用的循环嵌套是二重循环&#xff0c;外层循环称为 外循环&#xff0c;内层循环称为内循环。 二&#xff1a;双重循环&#xff1a; 1.双重循环结构 for (循…

for循环嵌套的三种用法

目前本人了解到的常用的三种for循环嵌套&#xff0c;以下是对这三种循环嵌套的理解&#xff1a; 一&#xff1a;内外循环联动 var arr [[10, 20, 30],[hello, hi, world],[a, b, c]]for (var i 0; i < arr.length;i){let innerArr arr[i];for(var j 0;j < innerArr…

多重for循环嵌套

for循环定义&#xff1a; 同过一个简单的表达式&#xff0c;来完成多次重复性的代码功能&#xff1b;格式如下&#xff1a; for&#xff08;变量初始值&#xff1b;变量取值范围&#xff1b;变量自增/自减&#xff09;{ //代码块&#xff1b; } 一个复杂的for循环中可以嵌…

JS中的for循环嵌套

for 循环 for语句也是一种前测试循环语句&#xff0c;但它具有在 执行循环之前初始化变量 和 定义循环后要执行的代码的能力 for循环中专门提供了位置来放置循环的三个表达式 定义一个循环需要做实现这三步&#xff1a; 1、初始化表达式 2、条件表达式 3、更新表达式 通…

【Python】循环语句 ⑦ ( for 循环嵌套 | continue 临时跳过本次循环 | break 结束循环 )

文章目录 一、for 循环嵌套1、for 循环嵌套语法2、for 循环嵌套 - range 简单示例3、for 循环嵌套 - 打印乘法表示例 二、continue 临时跳过本次循环1、continue 关键字简介2、代码示例 - continue 简单用法3、代码示例 - continue 在嵌套循环中使用 三、break 结束循环1、brea…

js中for循环嵌套

首先我们的for循环单个就是我们将内容全部输出出来执行的条件 1.首先声明初始值 2.设置条件 3.执行代码块 4.执行i 代码如下&#xff0c; <button type"button" onclick"tests()">测试10</button><div id"dom10"></d…

for循环,for循环嵌套

for循环 for(var i 0;i<10;i) for循环用于遍历对象&#xff0c;并将对象中的数拿出来 for循环的括号里用两个分号把它分成了三个部分&#xff0c;第一部分是循环变量&#xff0c;第二部分是循环的判断条件&#xff0c;第三部分是变量的变化规律&#xff08;即每循环一次变…

Java for循环和Java for循环嵌套详解

for 语句是应用最广泛、功能最强的一种循环语句。大部分情况下&#xff0c;for 循环可以代替 while 循环、do while 循环。 for 语句是一种在程序执行前就要先判断条件表达式是否为真的循环语句。假如条件表达式的结果为假&#xff0c;那么它的循环语句根本不会执行。for 语句…

Java for循环嵌套for循环,你需要懂的代码性能优化技巧

前言 本篇分析的技巧点其实是比较常见的&#xff0c;但是最近的几次的代码评审还是发现有不少兄弟没注意到。 所以还是想拿出来说下。 正文 是个什么场景呢&#xff1f; 就是 for循环 里面还有 for循环&#xff0c; 然后做一些数据匹配、处理 这种场景。 我们结合实例代码来…

Java for循环嵌套

一、需求 需求1&#xff1a;打印以下图形 **** **** **** for(int i 0;i<3;i){//控制行数for(int j 0;j<4;j){//控制列数System.out.print("*");}System.out.println(); } 需求2&#xff1a;打印以下图形 …