Quartz 相关线程
我们在前面文章中说到过Quartz涉及到的线程,但是散落在几篇文章中,不好找。而Quartz涉及到的线程对于理解Quartz也比较重要,所以今天专门提取出来单独说一下。
Quartz中的主要线程:
-
任务调度线程QuartzSchedulerThread
-
任务执行线程
-
Misfire处理线程
-
ClusterManager线程
任务调度线程 QuartzSchedulerThread
QuartzSchedulerThread是任务调度线程,他的职责是对满足触发条件(nextFireTimer到了)的注册到JobStore的Trigger分配给可用的任务执行线程去执行。
QuartzSchedulerThread启动
任务调度线程QuartzSchedulerThread是在调度器Scheduler创建的时候启动的。
应用层通过以下调用创建Scheduler:
Scheduler sche = new StdSchedulerFactory().getScheduler();
一般情况下通过StdSchedulerFactory构建Scheduler,getScheduler首先尝试从SchedulerRepository获取Schedule,首次运行获取不到,则通过instantiate()方法获取。
public Scheduler getScheduler() throws SchedulerException {if (cfg == null) {initialize();}SchedulerRepository schedRep = SchedulerRepository.getInstance();Scheduler sched = schedRep.lookup(getSchedulerName());if (sched != null) {if (sched.isShutdown()) {schedRep.remove(getSchedulerName());} else {return sched;}} sched = instantiate();return sched;
}
instantiate()方法特别特别特别长,几乎就是在这里完成Quartz所有相关组件的初始化的。
其中会创建QuartzScheduler,之后会将QuartzScheduler包装到stdScheduler中存入SchedulerRepository中。
instantiate()创建QuartzScheduler是调用的构造方法:
public QuartzScheduler(QuartzSchedulerResources resources, long idleWaitTime, @Deprecated long dbRetryInterval)throws SchedulerException {this.resources = resources;if (resources.getJobStore() instanceof JobListener) {addInternalJobListener((JobListener)resources.getJobStore());}this.schedThread = new QuartzSchedulerThread(this, resources); ThreadExecutor schedThreadExecutor = resources.getThreadExecutor();schedThreadExecutor.execute(this.schedThread);if (idleWaitTime > 0) { this.schedThread.setIdleWaitTime(idleWaitTime);}jobMgr = new ExecutingJobsManager();addInternalJobListener(jobMgr);errLogger = new ErrorLogger();addInternalSchedulerListener(errLogger);signaler = new SchedulerSignalerImpl(this, this.schedThread);getLog().info("Quartz Scheduler v." + getVersion() + " created.");
}
可以看到构造方法中创建了QuartzSchedulerThread对象,之后获取ThreadExecutor(一般情况下为DefaultThreadExecutor)并通过调用其execute方法启动QuartzSchedulerThread线程:
public class DefaultThreadExecutor implements ThreadExecutor {public void initialize() {}public void execute(Thread thread) {thread.start();}}
QuartzSchedulerThread的运行
其实就是他的run方法,我们也大概分析过,其主要逻辑是:
-
从作业执行线程池获取availThreadCount,也就是当前可用的线程数
-
调用JobStore的acquireNextTriggers方法,获取特定短时间(idleWaitTime,默认30秒)内可能需要被触发的,数量不超过availThreadCount的触发器
-
调用JobStore的triggersFired方法对获取到的可能需要被触发的触发器进行二次加工,再次获取到最终的待触发器结果集
-
循环处理最终的待处理触发器结果集中的每一个需要被触发的触发器
-
用JobRunShell包装该触发器,送给线程池执行该触发器关联的作业
好了,作业调度线程QuartzSchedulerThread我们就基本搞清楚了。
作业执行线程
Quartz的作业执行线程是放在线程池中进行管理的,默认是SimpleTreadPool,有关SimpleThreadPool我们前面专门有一篇文章介绍过,这里就不再赘述了。
作业执行线程和作业调度线程一样,也是在作业调度器Scheduler创建后立即启动,这个过程同样也是在StdSchedulerFactory的instantiate()方法中完成的:
instantiate()创建SimpleThreadPool之后会调用SimpleThreadPool的initialize方法,根据配置文件指定的任务执行线程数完成工作线程的初始化和启动。比如配置文件设置为10则初始化10个工作线程并逐个启动。作业执行线程的初始化及启动的详细过程请参考Quartz - SimpleThreadPool。
MisfireHandler线程
如果你的项目使用RAMJobStore,而不是JDBC-based JobStore(指需要持久化到数据库的JobStore),那么就不存在Misfire处理线程。
因为RAMJobStore在处理正常触发的过程中顺便就处理了Misfire,所以就不再需要其他处理机制了,这部分我们在前面的文章中也分析过Quartz - Misfire (for RAMJobstore)。
JDBC-based JobStore在处理正常触发的时候只获取未错过触发时间的触发器,对于错过触发时间的、也就是Misfire的触发器就需要另外的机制来处理。
Misfire处理线程就是Quartz采用JDBC-based JobStore的情况下用来处理错过触发时机的触发器的线程。
MisfireHandler启动
MisfireHandler定义在JobStoreSupport类中,JobStoreSupport是JobStore的JDBC-based JobStore的虚拟类,Quartz主要提供了两个基于JDBC的JobStore的实现:JobStoreTX、JobStoreCMT。JDBC-based JobStore详细内容请参考Quartz - JDBC-Based JobStore,今天主要分析MisfireHandler。
我们在应用中创建任务调度器Scheduler后需要调用他的start方法:
Scheduler sche = new StdSchedulerFactory().getScheduler();sche.scheduleJob(jobDetail,trigger);sche.start();
这个start方法会调用到JobStore的schedulerStarted()方法,如果我们应用中采用的是JDBC-based JobStore的话,会调用到JobStoreSupport的schedulerStarted(),其中会创建MisfireHandler之后调用MisfireHandler的initialize():
misfireHandler = new MisfireHandler();if(initializersLoader != null)misfireHandler.setContextClassLoader(initializersLoader);misfireHandler.initialize();
Misfire的initialize将创建好的MisFireHandle线程交给ThreadExecutor启动。
public void initialize() {ThreadExecutor executor = getThreadExecutor();executor.execute(MisfireHandler.this);}
MisfireHandle的运行
也就是MisfireHandle的run方法。处理逻辑和RAMJobStore处理misfired trigger的逻辑类似,只不过MisfireHandle的所有处理逻辑都是通过数据库操作完成的。
JDBC-Based JobStore对应的表结构我们会找机会专门分析,这里就不详细展开了。
MisfireHandle的run方法的主要逻辑为:
-
从数据库中获取在WAITING(等待执行)状态、下次执行时间小于msifiredtime(当前时间 - MisfireThreshold)的触发器,也就是错过触发时间的触发器
-
为了避免存在大量misfired trigger的情况下,一次处理太多数据影响其他正常触发器的执行,MisfireHandle线程每次仅获取部分而不是全部misfired trigger(参数maxToRecoverAtATime指定,默认为20)
-
对获取到的每一个错过执行时间的触发器(misfired trigger),调用触发器的updateAfterMisfire方法获取下次执行时间,updateAfterMisfire方法我们在上一篇讲Misfire处理策略的文章中说过,就是根据触发器的处理策略获取下次执行时间
-
updateAfterMisfire方法执行后,获取到的触发器的下次执行时间如果不为空的话,更新到数据库中,等待正常的任务执行线程调度执行
ClusterManager线程
与MisfiredHandle一样,ClusterManager线程也是JDBC-Based JobStore特有的。
顾名思义,ClusterManager线程与集群有关系,JDBC-Based JobStore是可以支持Quartz的集群部署的,在集群环境下,Quartz服务节点可能会down机、掉线,从而影响任务的执行,ClusterManager线程就是负责检查Quartz服务节点的在线状态的,如果发生掉线后,将该服务节点负责的触发器交给其他服务节点来处理。
具体逻辑等到我们分析完Quartz Cluster之后补充。
相关文章:
Quartz 相关线程
我们在前面文章中说到过Quartz涉及到的线程,但是散落在几篇文章中,不好找。而Quartz涉及到的线程对于理解Quartz也比较重要,所以今天专门提取出来单独说一下。 Quartz中的主要线程: 任务调度线程QuartzSchedulerThread 任务执…...
【QED】爱丽丝与混沌的无尽海
文章目录 题目题目描述输入输出格式数据范围测试样例 思路代码复杂度分析时间复杂度空间复杂度 题目 题目链接🔗 题目描述 如图所示,爱丽丝在一个3x3的迷宫之中,每个方格中标有 1 − 9 1-9 1−9各不相同的数字,爱丽丝可以从一格…...
IO模型学习
背景知识 Socket 套接字。 客户端和服务端通信时,客户端需要数据出口,服务端需要数据入口,这两个出入口就是Socket。数据接收方新建socket后需要绑定ip和端口号,这样客户端才能链接上socket。连接的过程就是 三次握手 FD file …...
Doxygen 使用指南
Doxygen 是一个文档生成工具,可以从源代码中的注释生成高质量的文档,支持多种编程语言(如 C/C、Python、Java 等)。以下是 Doxygen 的基本使用方法。 1. 安装 Doxygen 1.1 下载 Doxygen 访问 Doxygen 官网。根据操作系统选择合适…...
设计模式与游戏完美开发(2)
更多内容可以浏览本人博客:https://azureblog.cn/ 😊 该文章主主题内容均来自《设计模式与游戏完美开发》—蔡升达 第二篇 基础系统 第四章 游戏主要类——外观模式(Facade) 一、游戏子功能的整合 一个游戏程序常常由内部数个不…...
Coroutine 基础三 —— 结构化并发(二)
1、协程的结构化异常管理 如果一个协程抛异常,它所在的整个协程树上的其他协程(向上是父协程到根协程,向下是所有后代协程)都会被取消。因此协程发生异常的后果是十分严重的。 先讲原理,再说解决方案。 协程异常的处…...
GXUOJ-算法-第一次作业
1.整数划分 问题描述 GXUOJ | 整数划分 题解 #include<bits/stdc.h> using namespace std; const int N1010,mod1e97;int n; int f[N];int main(){cin>>n;f[0]1;for(int i1;i<n;i){for(int ji;j<n;j){f[j](f[j]f[j-i])%mod;}}cout<<f[n]; } 2.汉诺塔…...
Springboot项目Druid运行时动态连接多数据源的功能
项目支持多数据库连接是个很常见的需求,这不仅是要在编译前连已经知道的多个数据库,有时还要在程序运行时连后期增加的多个数据源来获得数据。 一、编译前注册数据库连接 1.引入依赖包 <!-- springboot 3.x --><dependency><groupId&g…...
字符串匹配——KMP算法
前言 刷到字符串匹配的力扣题了【28. 实现 strStr() 】,这题简单吧用库函数做就可以,说难吧,就得引出大名鼎鼎的线性匹配算法——KMP。 目录 KMP 算法背景与原理算法优势 前缀表1. 构建Next数组2. 搜索匹配 KMP 算法背景与原理 KMP&#x…...
Qt开发技术【下拉复选框 MultiSelectComboBox 自定义全选项】
继承ComboBox完成下拉复选框 自定义全选项 效果图 整个控件继承于QCombobox类。主要修改QLineEdit、QListWidget这两部分,QComboBox提供如下接口,可以将这两部分设置为新建的QLineEdit、QListWidget对象 CMultiSelectComboBox::CMultiSelectComboBo…...
20_HTML5 SSE --[HTML5 API 学习之旅]
HTML5 Server-Sent Events (SSE) 是一种技术,它允许服务器向浏览器推送更新。与传统的轮询不同,SSE提供了真正的单向实时通信通道:服务器可以主动发送数据到客户端,而不需要客户端发起请求。这对于实现实时更新的应用非常有用&…...
jetson Orin nx + yolov8 TensorRT 加速量化 环境配置
参考【Jetson】Jetson Orin NX纯系统配置环境-CSDN博客 一 系统环境配置: 1.更换源: sudo vi /etc/apt/sources.list.d/nvidia-l4t-apt-source.list2.更新源: sudo apt upgradesudo apt updatesudo apt dist-upgrade sudo apt-get updat…...
Android Studio IDE环境配置
需要安装哪些东西: Java jdk Java Downloads | OracleAndroid Studio 下载 Android Studio 和应用工具 - Android 开发者 | Android DevelopersAndroid Sdk 现在的Android Studio版本安装时会自动安装,需要注意下安装的路径Android Studio插件…...
PTA 7-2 0/1背包问题(回溯法) 作者 王东 单位 贵州师范学院
0/1背包问题。给定一载重量为W的背包及n个重量为wi、价值为vi的物体,1≤i≤n,要求重量和恰好为W具有最大的价值。 输入格式: 第一行输入背包载重量W及背包个数n,再依次输入n行,每行为背包重量wi和价值vi。 输出格式: 第一行输出装入背包内…...
Matlab环形柱状图
数据准备: 名称 数值 Aa 21 Bb 23 Cc 35 Dd 47 保存为Excel文件后: % Load data from Excel file filename data.xlsx; % Ensure the file is in the current folder or provide full path dataTable readtable(filena…...
【AI大模型】探索GPT模型的奥秘:引领自然语言处理的新纪元
目录 🍔 GPT介绍 🍔 GPT的架构 🍔 GPT训练过程 3.1 无监督的预训练语言模型 3.2 有监督的下游任务fine-tunning 🍔 小结 学习目标 了解什么是GPT.掌握GPT的架构.掌握GPT的预训练任务. 🍔 GPT介绍 GPT是OpenAI公…...
5.Python爬虫相关
爬虫 爬虫原理 爬虫,又称网络爬虫,是一种自动获取网页内容的程序。它模拟人类浏览网页的行为,发送HTTP请求,获取网页源代码,再通过解析、提取等技术手段,获取所需数据。 HTTP请求与响应过程 爬虫首先向…...
Windows系统上配置eNSP环境的详细步骤
华为eNSP(Enterprise Network Simulation Platform)是一款针对华为数通网络设备的网络仿真平台,用于辅助工程师进行网络技术学习、方案验证和故障排查等工作。以下是在Windows系统上配置eNSP环境的详细步骤: 1. 准备工作 下载安…...
Database.NET——一款轻量级多数据库客户端工具
文章目录 Database.NET简介下载使用使用场景总结 Database.NET简介 Database.NET 是一个功能强大且易于使用的数据库管理工具,适用于多种数据库系统。它为开发者和数据库管理员提供了一个统一的界面,可以方便地管理和操作不同类型的数据库。 支持的数据…...
新浪微博C++面试题及参考答案
多态是什么?请详细解释其实现原理,例如通过虚函数表实现。 多态是面向对象编程中的一个重要概念,它允许不同的对象对同一消息或函数调用做出不同的响应,使得程序具有更好的可扩展性和灵活性。 在 C 中,多态主要通过虚函…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
ArcGIS Pro制作水平横向图例+多级标注
今天介绍下载ArcGIS Pro中如何设置水平横向图例。 之前我们介绍了ArcGIS的横向图例制作:ArcGIS横向、多列图例、顺序重排、符号居中、批量更改图例符号等等(ArcGIS出图图例8大技巧),那这次我们看看ArcGIS Pro如何更加快捷的操作。…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
基于Java+VUE+MariaDB实现(Web)仿小米商城
仿小米商城 环境安装 nodejs maven JDK11 运行 mvn clean install -DskipTestscd adminmvn spring-boot:runcd ../webmvn spring-boot:runcd ../xiaomi-store-admin-vuenpm installnpm run servecd ../xiaomi-store-vuenpm installnpm run serve 注意:运行前…...
图解JavaScript原型:原型链及其分析 | JavaScript图解
忽略该图的细节(如内存地址值没有用二进制) 以下是对该图进一步的理解和总结 1. JS 对象概念的辨析 对象是什么:保存在堆中一块区域,同时在栈中有一块区域保存其在堆中的地址(也就是我们通常说的该变量指向谁&…...
【AI学习】wirelessGPT多任务无线基础模型摘要
收看了关于WirelessGPT多任务无线基础模型的演讲视频,边做一个记录。 应该说,在无线通信大模型的探索方面,有一个非常有益的尝试。 在沈学明院士带领下开展 https://www.chaspark.com/#/live/1125484184592834560...
Python基于蒙特卡罗方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融投资中,风险管理是确保资产安全和实现稳健收益的关键环节。随着市场波动性的增加,传统…...
