Quartz-QuartzSchedulerThread详解

Java | 一分钟掌握定时任务 | 6 - Quartz定时任务 前几篇介绍了单体架构的定时任务解决方式,但是现代软件架构由于业务复杂度高,业务的耦合性太强,已经由单体架构拆分成了分布式架构。因此,定时任务的架构也随之修改。而Quartz是分布式定时任务解决方案中使用简单,结构清晰,且不依赖第三方分布式调度中间件的。上车,mars酱带你车里细说~Scheduler:调度器。用来负责任务的调度;Job:任务。这是一个接口,业务代码继承Job接口并实现它的execute方法,是业务执行的主体部分;Trigger: 触发器。也是个接口,有两个触发器比较关键,一个是,另一个是。 阅读详情

QuartzSchedulerThread详解

QuartzSchedulerThread是一个线程类,负责查询并触发Triggers。

public class QuartzSchedulerThread extends Thread {
    QuartzSchedulerThread(QuartzScheduler qs, QuartzSchedulerResources qsRsrcs, boolean setDaemon, int threadPrio) {
        super(qs.getSchedulerThreadGroup(), qsRsrcs.getThreadName());
        ........
        paused = true;
        halted = new AtomicBoolean(false);
    }
}

该线程类的主要工作分为以下几个步骤:

  • 等待QuartzScheduler启动
  • 查询待触发的Trigger
  • 等待Trigger触发时间到来
  • 触发Trigger
  • 循环上述步骤
/*-----------------run()方法有删减----------------------*/
public void run() {
    while (!halted.get()) {
        // -------------------------------
        // 1 等待QuartzScheduler启动
        // -------------------------------
        synchronized (sigLock) {
            while (paused && !halted.get()) {
                // wait until togglePause(false) is called...
                sigLock.wait(1000L);
            }
        }

        // -------------------------------
        // 2 查询待触发的Trigger
        // -------------------------------
        int availThreadCount = qsRsrcs.getThreadPool().blockForAvailableThreads();
        if(availThreadCount > 0) { // will always be true, due to semantics of blockForAvailableThreads...
            // 查询未来(now + idletime)时间内待触发的Triggers
            // triggers是按触发时间由近及远排序的集合
            List<OperableTrigger> triggers = qsRsrcs.getJobStore().acquireNextTriggers(
                    now + idleWaitTime, Math.min(availThreadCount, qsRsrcs.getMaxBatchSize()), qsRsrcs.getBatchTimeWindow());
            if (triggers != null && !triggers.isEmpty()) {
                now = System.currentTimeMillis();
                long triggerTime = triggers.get(0).getNextFireTime().getTime();
                long timeUntilTrigger = triggerTime - now;
                // 通过循环阻塞,等待第一个Trigger触发时间
                while(timeUntilTrigger > 2) {
                    synchronized (sigLock) {
                        if (halted.get()) {
                            break;
                        }
                    }
                    now = System.currentTimeMillis();
                    timeUntilTrigger = triggerTime - now;
                }
            // 通知JobStore,这些Triggers将要被触发
            List<TriggerFiredResult> res = qsRsrcs.getJobStore().triggersFired(triggers);
            if(res != null)
                bndles = res;
            }
            // -------------------------------
            // 3 触发Triggers
            // -------------------------------
            for (int i = 0; i < bndles.size(); i++) {
                TriggerFiredResult result =  bndles.get(i);
                TriggerFiredBundle bndle =  result.getTriggerFiredBundle();
                JobRunShell shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
                shell.initialize(qs);
                qsRsrcs.getThreadPool().runInThread(shell);
            }
            continue; // while (!halted)
        } else { // if(availThreadCount > 0)
            // should never happen, if threadPool.blockForAvailableThreads() follows contract
            continue; // while (!halted)
        }
    } // while (!halted)
}

1 等待QuartzScheduler启动

synchronized (sigLock) {
    while (paused && !halted.get()) {
        // wait until togglePause(false) is called...
        sigLock.wait(1000L);
    }
}

循环检查paused && !halted.get()条件是否满足,否则释放sigLock对象的锁,并等待,一秒后重试。
当QuartzScheduler对象创建并调用start()方法时,将唤醒QuartzSchedulerThread线程,即可跳出阻塞块,继续执行。

/*QuartzScheduler*/
public void start() throws SchedulerException {
    ....
    schedThread.togglePause(false);
    ....
}

/*QuartzSchedulerThread*/
void togglePause(boolean pause) {
    synchronized (sigLock) {
        // 更改暂停状态
        paused = pause;
        if (paused) {
            signalSchedulingChange(0);
        } else {
            // 唤醒在sigLock上等待的所有线程
            sigLock.notifyAll();
        }
    }
}

2 查询待触发的Trigger

Quartz未雨绸缪,从JobStore中获取当前时间后移一段时间内(idle time + time window)将要触发的Triggers,以及在当前时间前移一段时间内(misfireThreshold)错过触发的Triggers(这里仅查询Trigger的主要信息)。被查询到的Trggers状态变化:STATE_WAITING-->STATE_ACQUIRED。结果集是以触发时间升序、优先级降序的集合。

public List<TriggerKey> selectTriggerToAcquire(Connection conn, long noLaterThan, long noEarlierThan, int maxCount)
        throws SQLException {
}
SELECT
    TRIGGER_NAME,
    TRIGGER_GROUP,
    NEXT_FIRE_TIME,
    PRIORITY
FROM
    QRTZ_TRIGGERS
WHERE
    SCHED_NAME = 'TestScheduler'
AND TRIGGER_STATE = ?
AND NEXT_FIRE_TIME <= ?
AND (
    MISFIRE_INSTR = - 1
    OR (
        MISFIRE_INSTR != - 1
        AND NEXT_FIRE_TIME >= ?
    )
)
ORDER BY
    NEXT_FIRE_TIME ASC,
    PRIORITY DESC

3 等待Trigger触发时间到来

因为上一步取得的Triggers是按时间排序的集合,所以取集合中的第一个,即触发时间最早的Trigger,等待其触发时间的到来。老套路while循环+wait实现。
不过需要注意的是,在此期间,可能有一些新的情况发生,比如说,新增了一个Trigger,并且该新增的Trigger比前面获取的触发时间都早,那么就需要将上面获取的Trigger释放掉(状态变化:STATE_ACQUIRED-->STATE_WAITING),然后重新查询Trggers

now = System.currentTimeMillis();
long triggerTime = triggers.get(0).getNextFireTime().getTime();
long timeUntilTrigger = triggerTime - now;
// 当触发时间距当前时间<=2 ms时,结束循环
while(timeUntilTrigger > 2) {
    synchronized (sigLock) {
        if (halted.get()) {
            break;
        }
        // 判断在此过程中是否有新增的并且触发时间更早的Trigger
        // 但是此处有个权衡,为了一个新增的的Trigger而丢弃当前已获取的是否值得?
        // 丢弃当前获取的Trigger并重新获取需要花费一定的时间,时间的长短与JobStore的实现有关。
        // 所以此处做了主观判断,如果使用的是数据库存储,查询时间假定为70ms,内存存储假定为7ms
        // 如果当前时间距已获得的第一个Trigger触发时间小于查询时间,则认为丢弃是不合算的。
        if (!isCandidateNewTimeEarlierWithinReason(triggerTime, false)) {
            try {
                // we could have blocked a long while
                // on 'synchronize', so we must recompute
                now = System.currentTimeMillis();
                timeUntilTrigger = triggerTime - now;
                // 距触发时间太早,先休息会吧
                if(timeUntilTrigger >= 1)
                    sigLock.wait(timeUntilTrigger);
            } catch (InterruptedException ignore) {
            }
        }
    }
    // 如果有新增的且触发时间更早的Trigger过来搅局,则释放上面已获取的Trigger,等待下一波查询
    if(releaseIfScheduleChangedSignificantly(triggers, triggerTime)) {
        break;
    }
    now = System.currentTimeMillis();
    timeUntilTrigger = triggerTime - now;
}

4 触发Trigger

前面提到过,先前只是获取Trigger的主要信息,其关联的Job、Calendar等信息是在触发前获取的。待Trigger所需信息验证、关联完成后,先行将Trigger的状态改为STATE_ACQUIRED-->STATE_COMPLETE。而后将Trigger封装后的TriggerFiredResult对象交由JobRunShell执行。

List<TriggerFiredResult> res = qsRsrcs.getJobStore().triggersFired(triggers);
for (int i = 0; i < bndles.size(); i++) {
    TriggerFiredResult result =  bndles.get(i);
    TriggerFiredBundle bndle =  result.getTriggerFiredBundle();
    JobRunShell shell = qsRsrcs.getJobRunShellFactory().createJobRunShell(bndle);
    shell.initialize(qs);
    qsRsrcs.getThreadPool().runInThread(shell);
}
Quartz与Spring集成——QuartzSchedulerThread的执行分析 前面说过当paused设置为false,QuartzSchedulerThread才正式启动,我们接着《Quartz与Spring集成——创建调度器》与《Quartz与Spring集成——启动调度器》中QuartzSchedulerThread启动的部分接着展开分析。 阅读详情

相关推荐

用示波器抓取同步Buck自举电容波形:从充电回路到SW节点振铃的完整分析

本文通过实测TI TPS54561和MPS MPQ4572同步Buck芯片,详细解析了自举电容在充电、维持和放电三个工作阶段的波形特征。重点揭示了电感泄放回路与充电回路重叠时,在SW节点引发的振铃现象及其产生机理,并提供了串联阻尼电阻等有效的工程抑制方法。文章结合示波器实测,为硬件工程师提供了从波形观测到问题调试的完整实践指南。

weixin_29276847的博客 372

Quartz 相关线程

JAVA任务调度 定时任务 Quartz 任务调度线程 任务执行线程 Misfire线程 Cluster Manager线程

weixin_44612246的博客 1009

从ResNet到EfficientNet:特征金字塔网络(FPN)在不同骨干网中的适配指南

本文深入探讨了特征金字塔网络(FPN)与ResNet、VGG、EfficientNet等主流骨干网络的适配策略。文章详细解析了FPN的核心原理,并针对不同骨干网络的特征提取特性,提供了通道对齐、参数量估算及迁移学习的系统化指南,旨在帮助开发者在目标检测等视觉任务中构建高性能、高效率的模型。

weixin_29047919的博客 220

Quartz 源码解析(三) —— QuartzSchedulerThread

一:大概内容 QuartzSchedulerThread 线程的创建和启动 线程run()方法逻辑 线程的协作 二:线程的创建和启动 这里主要回顾一下QuartzSchedulerThread是在什么时候创建的,又是在什么时候start的。 大概流程如下图 StdSchedulerFactory.instantiate():生产StdScheduler过程中会new一个QuartzScheduler实例 qs = new QuartzScheduler(rsrcs, idleWaitTi

li-zhang-yu的博客 1794

FDTD Solutions-入射波长模式

经常在文献中看到仿真光源使用TM/TE模式,这个确定是十分必要的,决定了仿真结果。 但是也不能局限于选择TM/TE,因为不同领域、不同软件之间的使用习惯和命名可能不同。并且在现代光波导器件中,几乎全部 都是混合模式,或者称之为quasi-TE(准TE模式)和quasi-TM(准TM模式),多数情况下都简称为TE模式和TM模式。要知道 到底是TE模式还是TM模式,需要查看其电场的主分量(即振幅...

HECHUANWANG的博客 1万+

家长必看!用RouterOS轻松屏蔽不良网站,保护孩子上网安全

本文为家长提供了一份详细的RouterOS路由器配置指南,旨在通过技术手段保护儿童上网安全。文章核心介绍了如何利用RouterOS的DNS静态条目功能,轻松实现禁止访问特定不良网站,并结合防火墙时间规则,为孩子构建一个纯净、可控的家庭网络环境,操作简单,无需专业背景。

weixin_28363123的博客 1266

Quartz执行逻辑(一)QuartzSchedulerThread介绍,quartz执行流程简介

1.背景 入职以来负责定时调度相关的业务,其底层用的是quartz框架。趁着最近有时间来总结一下quartz的相关逻辑。本篇就先简述一下quartz的执行逻辑。 2.执行逻辑叙述 quartz是通过一个调度线程不断的扫描数据库中的数据来获取到那些已经到点要触发的任务,然后调度执行它的。这个线程就是QuartzSchedulerThread类。其run方法中就是quartz的调度逻辑。 2.1获取触发器(Triggers) run方法中首先会判断quartz框架的线程池是否有可用的线程且没有被暂停,

卷福。的博客 6391

零信任架构实战:“持续验证+动态授权”全流程解析(附策略引擎联动图)

《零信任实战指南:7图拆解核心流程与行业案例》摘要:本文通过7张架构图系统解析零信任五大核心流程:强身份认证、设备检查、动态授权、持续监控和自动降权。对比金融(重数据审计)与制造业(重物理绑定)的落地差异,指出三大常见误区及解决方案。数据显示,零信任可使安全响应提速60%,泄露成本降低45%。文章提供6个月实施路线图,强调零信任是"智能信任"体系,建议企业分阶段改造,优先保护核心系统。附赠金融/制造行业策略模板及甘特图示例。(149字)

sara_han的博客 773

quartz框架(十)-QuartzSchedulerThread

QuartzSchedulerThread 本篇博文,博主将介绍QuartzSchedulerThread的相关内容。话不多说,直接进入正题。 什么是QuartzSchedulerThread? 从源码和该类的名称上,QuartzSchedulerThread首先是一个线程类,因此它继承了Thread类。从这一点上看,代码的自解释性非常重要。 从quartz框架整体上而言,QuartzSchedulerThread相当于管理者线程,它从JobStore中获取需要触发的任务,交给ThreadPool线程池去分

coderchenhao的博客 1433

如何合理评估信号过孔的残桩效应--Via Stub

设计中,之所以会去考察信号过孔的残桩效应(Via Stub),是因为它的存在导致了不需要的频率谐振,当这些谐振出现在所关注的信号通道的插入损耗中时,就会引发较为严重的信号完整性(SI)问题,那么Via Stub到底是如何引发SI问题的呢?

一只豌豆象的博客 4464

Allegro23.1新功能之软件自带阻抗计算工具操作指导

Allegro升级到了23.1后,软件新增了一个自带阻抗计算的功能,如下图。Propagation velocity:传输速度,单位是光速的多少倍。Dielectric constant输入介电常数,这里使用常规4。新功能之软件自带阻抗计算工具。如何使用,具体操作如下。

weixin_54787054的博客 782

Quartz源码解读:QuartzSchedulerThread

QuartzSchedulerThread在Quartz中的作用是负责任务的调度,读取用户设定的触发器并分配线程,使触发器能够按照预定时间执行 讲解代码前先简要梳理run()方法的执行流程: (1) 通过while循环检测调度是否应该停止,如果应该暂停则清空资源结束调度; (2) 通过while循环检测调度是否应该暂停,如果应该暂停,则在循环中阻塞,直至被外部环境唤醒; (3) 从线程池中...

qq_33265520的博客 1985

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

了解Quartz的QuartzSchedulerThread任务实现原理

Quartz是基于一条调度线程+任务工作线程池对任务进行分配调度执行,其中主要的执行流程在于线程实现。 QuartzSchedulerThread的初始化 通过代码可以发现: QuartzSched...

chuipu7376的博客 1279

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

分析Quartz(v2.3.2)QuartzSchedulerThread.run核心方法

最近项目中的定时任务,用Quartz框架取代了。最近也在学习Quartz框架这方面的知识,但是看代码过程有很多难以理解的地方。项目中使用数据库来存储的任务,本篇文章就从方法入手,分析任务是如何被选择执行的。

weixin_39439156的博客 656

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

精进Quartz源码—QuartzSchedulerThread.run() 源码分析(三)

QuartzSchedulerThread.run()是主要处理任务的方法!下面进行分析,方便自己查看! 我都是分析的jobStore 方式为jdbc的SimpleTrigger!RAM的方式类似分析方式!

阿飞云漫步 7607

Quartz源码——QuartzSchedulerThread.run() 源码分析(三)

QuartzSchedulerThread.run()是主要处理任务的方法!下面进行分析,方便自己查看! 我都是分析的jobStore 方式为jdbc的SimpleTrigger!RAM的方式类似分析方式! Quartz学习——scheduler.start()启动源码分析:http://blog.youkuaiyun.com/u010648555/article/details/53520314 Quart...

weixin_30364147的博客 181

DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 0 triggers

DEBUG org.quartz.core.QuartzSchedulerThread - batch acquisition of 0 triggers eclipse DEBUG模式下一直打印类似以上提示日志,非debug下却没有 解决方法: log4j.properties : log4j.logger.org.quartz=INFO :log4j.logger.org.quar

lipingping951462的专栏 2万+

quartz问题备份

QuartzSchedulerThread

zxk364961978的专栏 342

Quartz源码分析(下)

上一篇文章中,我们分析了Quartz框架的Job和Trigger的源码实现,上篇也说到,Quartz的核心代码是Scheduler,在本篇中,我们会分析一下Scheduler的源码实现。 文章目录1、核心入口类:QuartzScheduler2、调度线程类:QuartzSchedulerThread3、任务执行线程池:SimpleThreadPool4、任务执行线程类:WorkerThread5...

CringKong的博客 1381

Quartz-start启动流程

转载请注明出处:https://www.cnblogs.com/Dorae/p/9357180.html 本文出自:后端知识点梳理-定时任务Quartz篇章 Quartz 启动流程解析 若Quartz是配置在spring中,当服务器启动时,就会装载相关的bean。SchedulerFactoryBean实现了InitializingBean接口,因此在初始化Bean的时候会执行af...

WandaZw的博客 1743

Quartz Scheduler

SchedulerFactory & Scheduler & QuartzScheduler & QuartzSchedulerThread & ThreadExecutor(DefaultThreadExecutor) & ThreadPool(SimpleThreadPool and WorkThread)是Quartz调度任务的核心类, Schedul...

来啊 快活啊 5272

quartz启动流程

quartz启动流程 若quartz是配置在spring中,当服务器启动时,就会装载相关的bean。SchedulerFactoryBean实现了InitializingBean接口,因此在初始化bean的时候,会执行afterPropertiesSet方法,该方法将会调用Scheduler...

chensen4476的博客 1608

quartz(7)-源码分析

定时器启动 上图通过spring加载quartz <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> ... </bean> SpringContext在加载SchedulerFactoryBean时...

weixin_30455067的博客 139
上一篇: Eclipse导出安装的插件
下一篇: Quartz-基础组件
Impler
博客等级 码龄8年 4粉丝 7原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值