当前位置: 首页 > news >正文

Quartz中禁止并发机制源码级解析

文章目录

Quartz进行任务调度时通常会要求一个任务禁止并发执行,此时只需要在Job类上面添加一个注解@DisallowConcurrentExecution即可。在保存到数据库里面时,对应QRTZ_JOB_DETAILS表中的IS_NONCONCURRENT字段的值为1(true)。那么这里是怎么控制的呢?
在Quartz项目搭建与任务执行源码分析中详细介绍一个正常流程过程中涉及的表和状态的变化过程。总结的表格如下

表名scheduleJobacquireNextTriggerstriggersFiredtriggeredJobComplete
QRTZ_TRIGGERSWAITING(CAS)ACQUIREDWAITINGWAITING
QRTZ_FIRED_TRIGGERSACQUIREDEXECUTING

而对于禁止并发的任务,其状态则如下所示

表名scheduleJobacquireNextTriggerstriggersFiredtriggeredJobComplete
QRTZ_TRIGGERSWAITING(CAS)ACQUIREDBLOCKEDWAITING
QRTZ_FIRED_TRIGGERSACQUIREDEXECUTING
  1. 调度任务在查询待触发任务时,如果同时查出来一个任务对应的多个触发器,只有第一个有效(通过acquiredJobKeysForNoConcurrentExec集合保证唯一性)
    org.quartz.impl.jdbcjobstore.JobStoreSupport#acquireNextTrigger
Set<JobKey> acquiredJobKeysForNoConcurrentExec = new HashSet<JobKey>();
// ... 其他代码省略// If trigger's job is set as @DisallowConcurrentExecution, and it has already been added to result, then// put it back into the timeTriggers set and continue to search for next trigger.JobKey jobKey = nextTrigger.getJobKey();JobDetail job = getDelegate().selectJobDetail(conn, jobKey, getClassLoadHelper());if (job.isConcurrentExectionDisallowed()) {if (acquiredJobKeysForNoConcurrentExec.contains(jobKey)) {continue; // next trigger} else {acquiredJobKeysForNoConcurrentExec.add(jobKey);}}
  1. 任务触发时,将不支持并发机制的触发器的状态由 WAITING -> BLOCKED,这样调度线程查询待触发任务时便不会满足条件(调度任务只会查询WAITING状态的触发器)

org.quartz.impl.jdbcjobstore.JobStoreSupport#triggersFired

if (job.isConcurrentExectionDisallowed()) {state = STATE_BLOCKED;force = false;try {getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getKey(),STATE_BLOCKED, STATE_WAITING);getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getKey(),STATE_BLOCKED, STATE_ACQUIRED);getDelegate().updateTriggerStatesForJobFromOtherState(conn, job.getKey(),STATE_PAUSED_BLOCKED, STATE_PAUSED);} catch (SQLException e) {throw new JobPersistenceException("Couldn't update states of blocked triggers: "+ e.getMessage(), e);}
} 
  1. 任务执行完毕,会将触发器的状态修改回来 BLOCKED -> WAITING

org.quartz.core.QuartzScheduler#notifyJobStoreJobComplete
执行org.quartz.spi.JobStore#triggeredJobComplete方法

if (jobDetail.isConcurrentExectionDisallowed()) {getDelegate().updateTriggerStatesForJobFromOtherState(conn,jobDetail.getKey(), STATE_WAITING,STATE_BLOCKED);getDelegate().updateTriggerStatesForJobFromOtherState(conn,jobDetail.getKey(), STATE_PAUSED,STATE_PAUSED_BLOCKED);signalSchedulingChangeOnTxCompletion(0L);
}

除了以上地方,在Quartz启动时,org.quartz.impl.jdbcjobstore.JobStoreSupport#recoverJobs首先会将一些阻塞的任务都修改为WAITING,防止系统崩溃导致任务未执行完而来不及恢复禁止并发任务的状态。

// update inconsistent job states
int rows = getDelegate().updateTriggerStatesFromOtherStates(conn,STATE_WAITING, STATE_ACQUIRED, STATE_BLOCKED);rows += getDelegate().updateTriggerStatesFromOtherStates(conn,STATE_PAUSED, STATE_PAUSED_BLOCKED, STATE_PAUSED_BLOCKED);getLog().info("Freed " + rows+ " triggers from 'acquired' / 'blocked' state.");

另外在项目启动、新增触发器、定时任务补偿等所有涉及数据库操作Trigger中都会执行org.quartz.impl.jdbcjobstore.JobStoreSupport#storeTrigger操作,而其中都会检查状态,checkBlockedState.

if (job.isConcurrentExectionDisallowed() && !recovering) { state = checkBlockedState(conn, job.getKey(), state);
}if (existingTrigger) {getDelegate().updateTrigger(conn, newTrigger, state, job);
} else {getDelegate().insertTrigger(conn, newTrigger, state, job);
}

checkBlockedState中,就会根据是否禁止并发并判断是否任务已经执行(通过selectFiredTriggerRecordsByJob查询QRTZ_FIRED_TRIGGERS表中是否有数据)返回BLOCKED状态,org.quartz.impl.jdbcjobstore.JobStoreSupport#checkBlockedState

/*** Determines if a Trigger for the given job should be blocked.  * State can only transition to STATE_PAUSED_BLOCKED/BLOCKED from * PAUSED/STATE_WAITING respectively.* * @return STATE_PAUSED_BLOCKED, BLOCKED, or the currentState. */
protected String checkBlockedState(Connection conn, JobKey jobKey, String currentState)throws JobPersistenceException {// State can only transition to BLOCKED from PAUSED or WAITING.if ((!currentState.equals(STATE_WAITING)) &&(!currentState.equals(STATE_PAUSED))) {return currentState;}try {List<FiredTriggerRecord> lst = getDelegate().selectFiredTriggerRecordsByJob(conn,jobKey.getName(), jobKey.getGroup());if (lst.size() > 0) {FiredTriggerRecord rec = lst.get(0);if (rec.isJobDisallowsConcurrentExecution()) { // OLD_TODO: worry about failed/recovering/volatile job  states?return (STATE_PAUSED.equals(currentState)) ? STATE_PAUSED_BLOCKED : STATE_BLOCKED;}}return currentState;} catch (SQLException e) {throw new JobPersistenceException("Couldn't determine if trigger should be in a blocked state '"+ jobKey + "': "+ e.getMessage(), e);}}

从上面可以看到,基本上在整个操作触发器的生命周期,都会考虑当前任务是否禁止并发操作,通过QRTZ_FIRED_TRIGGERS表是否有数据判断任务是否执行、不支持并发机制的任务在执行时将QRTZ_TRIGGERS中的状态改为BLOCKED以避免再次被触发。

http://www.lryc.cn/news/105434.html

相关文章:

  • 为什么从公有云迁移到私有云的越来越多?
  • 用shell实现MySQL分库分表操作
  • php 适配器模式
  • Scratch Blocks自定义组件之「下拉图标」
  • Robot Framweork之UI自动化测试---分层设计
  • MySQL8.0/8.x更新用户密码命令
  • 【MySQL】下载安装以及SQL介绍
  • 算法题--二叉树(二叉树的最近公共祖先、重建二叉树、二叉搜索树的后序遍历序列)
  • mysql的基础面经-索引、事务
  • Windows下双网卡配置静态路由,实现内外网同时使用
  • Spring整合Mybatis、Spring整合JUnit
  • Devops系统中jira平台迁移
  • 【雕爷学编程】MicroPython动手做(29)——物联网之SIoT
  • LAXCUS分布式操作系统引领科技潮流,进入百度首页
  • Linux--按行读取数据:fgets
  • express学习笔记5 - 自定义路由异常处理中间件
  • filebeat介绍
  • 使用SSM框架实现个人博客管理平台以及实现Web自动化测试
  • 【深度学习】MAT: Mask-Aware Transformer for Large Hole Image Inpainting
  • PyTorch BatchNorm2d详解
  • 慕课网Go-4.package、单元测试、并发编程
  • [JavaWeb]SQL介绍-DDL-DML
  • git源码安装(无sudo权限)
  • Web3 叙述交易所授权置换概念 编写transferFrom与approve函数
  • Redis系列二:Clion+MAC+Redis环境搭建
  • Linux下 Docker容器引擎基础(2)
  • 现场服务管理系统有哪些?5个现场服务管理软件对比
  • leetcode 912.排序数组
  • 利用MMPreTrain微调图像分类模型
  • express学习笔记3 - 三大件