Quartz的分布式并发控制依赖数据库行级锁而非Java锁,核心是多个scheduler实例竞争TRIGGER_ACCESS锁以安全认领触发器。获取锁后原子执行查询、更新状态、释放锁三步操作,确保同一触发器不被重复执行;STATE_ACCESS锁则保障元数据操作一致性。

Quartz 底层的多线程并发控制,并不是靠 Java 层面的 synchronized 或 ReentrantLock 实现的,而是依赖数据库行级锁 + 分布式状态协同,实现跨 JVM 实例的细粒度卡控。关键不在“线程怎么抢锁”,而在“多个 scheduler 实例怎么安全地抢同一个触发器执行权”。
调度线程不直接竞争任务,而是竞争数据库里的“触发器访问锁”
每个 Quartz scheduler 实例启动后,会周期性扫描 JobStore(如 JDBCJobStore)中待触发的 Trigger。但不会直接取走任务——它先尝试获取 {sched_name}.LOCKS 表中名为 TRIGGER_ACCESS 的行级锁:
- MySQL 执行类似
SELECT * FROM QRTZ_LOCKS WHERE SCHED_NAME = 'MyScheduler' AND LOCK_NAME = 'TRIGGER_ACCESS' FOR UPDATE - 只有成功加锁的实例,才能继续读取、标记、更新即将触发的 Trigger(比如把
STATE从WAITING改为ACQUIRED) - 其他实例在该行锁释放前,只能等待或跳过本次扫描
一个触发器只被一个实例“认领”,避免重复执行
当某实例拿到 TRIGGER_ACCESS 锁后,会原子性地执行三步操作(本质是单条 SQL 或事务内完成):
- 查出状态为
WAITING且下次触发时间 ≤ 当前时间的 Trigger 列表 - 对这批 Trigger 批量更新状态为
ACQUIRED,并设置INSTANCE_NAME字段指向自己 - 释放
TRIGGER_ACCESS锁,让其他实例可参与下一轮竞争
这个过程确保:即使 10 个 scheduler 实例同时运行,同一 Trigger 在同一时刻最多只被一个实例“认领”,从根本上杜绝重复触发。
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
Worker 线程池负责执行,但不参与分布式协调
被认领的 Trigger 会交由本实例内的 worker 线程池(ThreadPool)异步执行:
- worker 线程只管调用
Job.execute(),不接触数据库锁逻辑 - 线程数量由
org.quartz.threadPool.threadCount配置,影响单节点并发执行能力 - 若 Job 本身有共享资源,仍需在业务代码里加锁(如
synchronized或ReentrantLock),这是应用层责任,和 Quartz 分布式锁无关
状态访问锁(STATE_ACCESS)保障元数据一致性
除触发外,还有两类操作需额外保护:
- 修改 Scheduler 状态(如 pause/resume)时,需获取
STATE_ACCESS锁 - 恢复失败 Job、清理历史数据等维护操作,也依赖对应锁防止冲突
- 这些锁与
TRIGGER_ACCESS是独立的行,互不影响,实现不同维度的细粒度隔离
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










