调度介绍 - Quartz是怎么调起来的

简介: Quartz调度核心类QuartzSchedulerThread,是一个线程。在线程启动后,通过while循环不断去触发作业的执行。ps:源码版本2.3.2,

1.前言

上篇文章,我们介绍了quartz的调度过程及关键类。我们知道quartz作为一个调度框架,调度是框架的核心。
由上篇我们知道,在Job被调用之前,经过了几个过程,调度器启动,调度器将job和trigger加入调度器。之后就发现作业被调度了。这里再思考一下调度背后是什么?如果让我们去实现一个调度该怎么做。


2.正文

上文中Scheduler是通过Spring注入的,我们没法了解Scheduler的初始化过程。所以,我们找了个官方示例,示例中可以看到在Job被调度之前有如下3行代码:

SchedulerFactory schedFact = new org.quartz.impl.StdSchedulerFactory();

Scheduler sched = schedFact.getScheduler();

sched.start();

第一行,源码中StdSchedulerFactory的无参构造方法是个空方法,不管他。
第二行,schedFact.getScheduler()方法非常关键,该方法中初始化了QuartzScheduler,并返回了Scheduler。
第三行,调度器调用start()方法,开始启动。

2.1 调度准备

多图预警

第一,我们可以看到QuartzScheduler在初始化的时候,初始化并启动了调度线程QuartzSchedulerThread,并且该线程的标识halted = false,paused = true。源码截图如下:
1.png
2.png
3.png
7.png

第二,我们看类之间的关系。StdScheduler是Scheduler的实现类,也是QuartzScheduler的代理类。
由此我们可知,Scheduler的start()方法实际是调用了QuartzScheduler的start()方法。而start()方法中将调度线程QuartzSchedulerThread的paused = false。也就是说退出了while循环里嵌套的while,线程开始向下运行。源码截图如下:
4.1.png
4.png
5.png
5.5.png
6.png

以上是quartz调度前的准备工作。
小结:QuartzSchedulerThread线程启动后,run方法开始运行,因为halted = false故进入第一个while循环,因为halted = false,paused = true接着会进入第二个while循环,第二个循环wait-wait-wait......不停的wait,一直等到start()方法将paused设置为false。则退出嵌套的循环,开始往下运行,而往下才开始真正的调度。

2.2 准备完成,开始调度

先看一下run方法全貌:
9png.png
总体可以分为三块,前后两个同步代码块,中间夹杂了一些代码。第一个同步代码块就是刚才的嵌套while循环,用于等待调度启动,而另外一个功能类似,也是等待。这里我们重点关注的是中间的部分,图片中红框标注的。变量availThreadCount由名称可以知道,是线程池当前可用的线程数量。availThreadCount如果大于0则进入if分支执行,然后contine。这里好理解,就是去确认下当前是否有可用线程,如果有则开始进入调度逻辑。这部分代码挺长从279行到414行,我们截取关键部分看一下:
第一步,查询即将到触发时间的trigger
11.png
第二步,判断trigger是否到时间
10.png
第三步,标记trigger已触发
12png.png
第四步,通过线程池执行Job
13.png
最后,下一轮while循环。

2.3 小结

调度线程QuartzSchedulerThread进入while的无限循环,查询JobStore中即将到期的trigger,判断trigger是否到达触发时间,如果到达则通过线程池异步执行trigger绑定的Job。

3. 后记

Quartz作为一个调度框架,提供的丰富的特性,也是基于此源码量多也杂。但是作为一个调度框架核心逻辑仍是调度,我们这里通过看源码,略微一窥他调度的背后是什么。
以下是看源码时,觉得比较关键的类

  • Scheduler:调度器接口。
  • QuartzScheduler:真正调度器的实现,提供了调度器的启停、作业增删等丰富的功能。
  • StdScheduler:调度器的官方实现,实现了Scheduler,也是QuartzScheduler的代理类。
  • QuartzSchedulerThread:核心调度线程,也就是他马不停蹄的去运行Job。
  • ThreadPool:官方的线程池,用于执行Job。
  • JobRunShell:持有TriggerFiredBundle,TriggerFiredBundle持有JobDetail。JobRunShell是一个线程的实现,会真正的调用Job的execute(JobExecutionContext context)方法。

如果对你有bangz帮助,点个赞再走吧

相关文章
|
Java 应用服务中间件 网络安全
Tomcat配置ssl协议及遇到的问题https页面无法访问
Tomcat配置ssl协议及遇到的问题https页面无法访问
|
消息中间件 存储 负载均衡
一文读懂RocketMQ的高可用机制——消息发送高可用
一文读懂RocketMQ的高可用机制——消息发送高可用
648 1
|
关系型数据库 MySQL 数据库
OceanBase数据库常见问题之脚本执行失败如何解决
OceanBase 是一款由阿里巴巴集团研发的企业级分布式关系型数据库,它具有高可用、高性能、可水平扩展等特点。以下是OceanBase 数据库使用过程中可能遇到的一些常见问题及其解答的汇总,以帮助用户更好地理解和使用这款数据库产品。
|
10月前
|
网络虚拟化 数据安全/隐私保护 数据中心
对比了思科和华为网络设备的基本配置、接口配置、VLAN配置、路由配置、访问控制列表配置及其他重要命令
本文对比了思科和华为网络设备的基本配置、接口配置、VLAN配置、路由配置、访问控制列表配置及其他重要命令,帮助网络工程师更好地理解和使用这两个品牌的产品。通过详细对比,展示了两者的相似之处和差异,强调了持续学习的重要性。
516 2
|
小程序 JavaScript 前端开发
【微信小程序】--WXML & WXSS & JS 逻辑交互介绍(四)
【微信小程序】--WXML & WXSS & JS 逻辑交互介绍(四)
|
安全 应用服务中间件 Linux
windows配置supervisor实现nginx自启
windows配置supervisor实现nginx自启
325 0
|
前端开发 JavaScript
React中封装echarts图表组件以及自适应窗口变化
React中封装echarts图表组件以及自适应窗口变化
361 1
|
Oracle Java 关系型数据库
ARM架构下的Docker环境,OpenJDK官方没有8版本镜像,如何完美解决?
ARM处理器环境下,想把Java应用运行在Docker环境,首先要找好基础镜像,如果您的Java应用是基于JDK8版本,就会面临找不到8版本JDK镜像的问题,本文就来解决此问题
1477 0
ARM架构下的Docker环境,OpenJDK官方没有8版本镜像,如何完美解决?
|
XML 网络协议 关系型数据库
Nmap扫描基础常用命令(包含进阶使用)
Nmap扫描基础常用命令(包含进阶使用)
1868 1