寻求@DisallowConcurrentExecution的有效替代方案(StatefulJob已废弃)
替代@DisallowConcurrentExecution的可行方案
方案一:通过JobDetail构建器禁用并发执行
这是最贴合原注解功能的替代方式,无需修改Job类代码,只需在创建JobDetail时通过Quartz API显式配置禁用并发:
// 创建JobDetail时调用disallowConcurrentExecution()方法 JobDetail jobDetail = JobBuilder.newJob(AJob.class) .withIdentity("aJob", "group1") .disallowConcurrentExecution() // 效果等价于@DisallowConcurrentExecution注解 .build();
配置后,Quartz会保证同一个JobDetail的任务实例不会并发执行,和原注解的行为完全一致,且不需要在Job类上添加任何注解。
方案二:自定义锁实现并发控制
如果需要更灵活的控制(比如跨节点的全局并发限制),可以在Job的execute方法内部实现锁机制:
单节点场景(本地锁)
使用Java的ReentrantLock实现本地锁,确保同一JVM内只有一个任务实例在执行:
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.time.ZonedDateTime; import java.util.concurrent.locks.ReentrantLock; public class AJob implements Job { private static final Logger logger = LoggerFactory.getLogger(AJob.class); // 类级别的锁,确保所有任务实例共享同一锁 private static final ReentrantLock lock = new ReentrantLock(); @Override public void execute(JobExecutionContext context) throws JobExecutionException { if (!lock.tryLock()) { logger.info("任务正在执行中,本次触发跳过"); return; } try { logger.info("Scheduler output: " + ZonedDateTime.now().toString()); // 模拟任务执行耗时 Thread.sleep(5000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new JobExecutionException(e); } finally { lock.unlock(); } } }
集群场景(分布式锁)
如果Quartz部署在集群环境,需要用分布式锁(如Redis锁)保证全局唯一执行:
import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import redis.clients.jedis.Jedis; import java.time.ZonedDateTime; import java.util.UUID; public class AJob implements Job { private static final Logger logger = LoggerFactory.getLogger(AJob.class); // 锁的唯一标识key private static final String LOCK_KEY = "quartz:aJob:lock"; // 锁超时时间,避免任务异常导致锁无法释放 private static final int LOCK_TIMEOUT = 60; @Override public void execute(JobExecutionContext context) throws JobExecutionException { Jedis jedis = null; String requestId = UUID.randomUUID().toString(); try { jedis = new Jedis("localhost", 6379); // 尝试获取分布式锁 String result = jedis.set(LOCK_KEY, requestId, "NX", "EX", LOCK_TIMEOUT); if ("OK".equals(result)) { logger.info("Scheduler output: " + ZonedDateTime.now().toString()); // 模拟任务执行耗时 Thread.sleep(5000); } else { logger.info("任务正在集群中执行,本次触发跳过"); } } catch (Exception e) { throw new JobExecutionException(e); } finally { // 仅持有锁的请求可释放锁 if (jedis != null && requestId.equals(jedis.get(LOCK_KEY))) { jedis.del(LOCK_KEY); jedis.close(); } } } }
注:分布式锁可根据实际使用的缓存中间件调整实现,比如用Redisson能简化锁的续期等逻辑,上述为基础示例。
内容的提问来源于stack exchange,提问作者stackoverflow
相关产品推荐
相关产品推荐

