@DisallowConcurrentExecution 用于禁止相同 JobKey 的 JobDetail 并发执行,Quartz 通过数据库状态标记与内存集合跳过重叠触发,不依赖线程锁;需配合持久化 JobStore 和 @PersistJobDataAfterExecution 才可靠生效。

@DisallowConcurrentExecution 的作用不是“手动阻止”并发,而是告诉 Quartz:这个 Job 类对应的 JobDetail 不允许被同时触发执行。Quartz 在底层调度逻辑中主动跳过后续重叠触发,而不是等任务跑起来再加锁或排队。
它管的是 JobDetail,不是 Job 类本身
同一个 Job 实现类(比如 SyncDataJob)可以被定义成多个 JobDetail(例如同步 user 表、同步 order 表),只要它们的 JobKey 不同(即 name + group 组合唯一),就互不影响。加了注解,只禁止“相同 JobKey”的多次触发同时运行。
- ✅ 允许:SyncDataJob(job1/groupA)和 SyncDataJob(job2/groupB)并行执行
- ❌ 禁止:SyncDataJob(job1/groupA)的第二次触发在第一次还没结束时启动
底层靠状态控制,不依赖线程锁
Quartz 在数据库层面(如 QRTZ_TRIGGERS 表)把该 Job 的触发器状态设为 BLOCKED,而不是 WAITING;同时在内存中维护一个 acquiredJobKeysForNoConcurrentExec 集合。当调度线程扫描待触发任务时,发现 Job 已标记非并发且已在执行中,就直接跳过这次触发,继续找下一个可用任务。
- 不消耗额外线程资源去“等待”,也不阻塞调度主线程
- 不会出现 synchronized 可能引发的死锁或线程饥饿
- 即使任务执行超时、崩溃或未正常完成,Quartz 也能通过恢复机制清理状态,避免永久阻塞
必须配合 JobStore 持久化才可靠
如果用的是 RAMJobStore(内存存储),集群或重启后状态丢失,@DisallowConcurrentExecution 就失效了。真正起作用的前提是:
立即学习“Java免费学习笔记(深入)”;
- 使用
JobStoreTX或ClusteredJDBCJobStore - 数据库表
QRTZ_JOB_DETAILS.IS_NONCONCURRENT = 1 - JobDetail 创建时已通过注解或
setConcurrentExecutionDisallowed(true)显式设置
别和 @PersistJobDataAfterExecution 拆开用
如果 Job 中用了 JobDataMap 传参或存中间状态,又希望下一次执行能读到上次更新后的值,就必须同时加 @PersistJobDataAfterExecution。否则 Quartz 不会持久化修改,下次拿到的还是初始值——而并发控制失效时,多个实例写同一份 Map 更容易出错。
- 两个注解常一起出现,不是巧合,是设计配套
- 单独加 @PersistJobDataAfterExecution 而没禁并发,可能造成数据覆盖或脏读


















