You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中如何配置Quartz确保任务同一时间仅执行一次?

确保Quartz任务同一时间仅执行一次的配置方案

针对你的场景(支持cron调度+手动触发,@DisallowConcurrentExecution不足以满足需求),可以从以下几个层面入手解决:

1. 单节点场景:正确结合Quartz注解

首先确保Job类正确使用注解组合,覆盖Quartz调度层面的并发控制:

  • @DisallowConcurrentExecution:阻止Quartz为同一个JobDetail实例调度多个并发执行的任务,但仅当所有触发(包括手动)都通过Quartz调度器触发时生效。
  • @PersistJobDataAfterExecution:配合前者使用,确保JobDataMap的状态在执行后持久化,避免并发判断逻辑失效。

示例代码:

@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class YourQuartzJob implements Job {
    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 任务执行逻辑
    }
}

注意:如果手动触发是直接调用Job的execute方法(而非通过Quartz的Scheduler.triggerJob()),该注解组合将失效,因为绕开了Quartz的调度控制。

2. 手动触发前强制检查任务运行状态

如果手动触发是通过业务代码发起,必须先检查当前任务是否正在执行,再决定是否触发:
通过Quartz的Scheduler API获取实时执行状态:

@Autowired
private Scheduler scheduler;

public boolean triggerJobManually(String jobName, String jobGroup) throws SchedulerException {
    JobKey jobKey = new JobKey(jobName, jobGroup);
    
    // 检查当前是否有该任务的实例正在执行
    List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
    for (JobExecutionContext ctx : executingJobs) {
        if (ctx.getJobDetail().getKey().equals(jobKey)) {
            return false; // 任务正在执行,拒绝触发
        }
    }
    
    // 手动触发任务
    scheduler.triggerJob(jobKey);
    return true;
}

3. 分布式场景:引入分布式锁

如果项目是多节点部署,仅靠Quartz自身注解无法跨节点控制并发,需要引入分布式锁(如Redis):
在Job的execute方法开头添加锁逻辑,确保同一时间只有一个节点能执行任务:

@DisallowConcurrentExecution
@PersistJobDataAfterExecution
public class YourDistributedQuartzJob implements Job {
    @Autowired
    private StringRedisTemplate redisTemplate;

    private static final String LOCK_KEY = "quartz:job:your_job_lock";
    // 锁超时时间需大于任务最长预估执行时间
    private static final long LOCK_TIMEOUT = 30000; 

    @Override
    public void execute(JobExecutionContext context) throws JobExecutionException {
        // 尝试获取分布式锁
        Boolean lockAcquired = redisTemplate.opsForValue()
            .setIfAbsent(LOCK_KEY, "locked", LOCK_TIMEOUT, TimeUnit.MILLISECONDS);
        
        if (lockAcquired == null || !lockAcquired) {
            // 未获取到锁,说明任务正在执行,直接返回
            return;
        }
        
        try {
            // 任务执行逻辑
        } finally {
            // 执行完成后释放锁
            redisTemplate.delete(LOCK_KEY);
        }
    }
}

4. 集群模式下配置Quartz JobStore

如果使用Quartz集群部署,需配置基于数据库的JobStore,确保任务状态在集群节点间同步:
在application.yml中添加配置:

spring:
  quartz:
    job-store-type: jdbc
    properties:
      org:
        quartz:
          scheduler:
            instanceId: AUTO
          jobStore:
            class: org.quartz.impl.jdbcjobstore.JobStoreTX
            driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
            tablePrefix: QRTZ_
            isClustered: true
            clusterCheckinInterval: 10000

集群模式下,Quartz会通过数据库锁确保同一任务不会在多个节点同时执行,配合@DisallowConcurrentExecution可实现更可靠的并发控制。


内容的提问来源于stack exchange,提问作者Daniel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 10:15:47