Quartz Cron调度器在指定时间被重复触发两次问题排查
解决Quartz Cron调度器重复触发的问题
你的Quartz Cron调度器每天13:00被触发两次,我先梳理下你的配置和代码,再分析几个常见的原因及对应的解决办法:
你的配置与代码
Spring Bean配置
<bean id="runMeTask" class="com.ascent.fieldomobify.cornScheduler.RunMeTask"/> <bean name="runMeJob" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="com.ascent.fieldomobify.cornScheduler.RunMeJob" /> <property name="jobDataAsMap"> <map> <entry key="runMeTask" value-ref="runMeTask" /> </map> </property> </bean> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean"> <property name="jobDetail" ref="runMeJob"/> <property name="cronExpression" value="0 0 13 * * ?" /> </bean> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> </bean>
RunMeJob实现类
public class RunMeJob extends QuartzJobBean { private RunMeTask runMeTask; public void setRunMeTask(RunMeTask runMeTask) { this.runMeTask = runMeTask; } protected void executeInternal(JobExecutionContext context) throws JobExecutionException { try { runMeTask.printMe(); } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
RunMeTask实现类
public class RunMeTask{ @Autowired WorkOrderController workorderContoller; public void setWorkorderContoller(WorkOrderController workorderContoller) { this.workorderContoller = workorderContoller; } public void printMe() throws ParseException { workorderContoller.printSysOut(); } }
常见原因及解决办法
1. Spring容器重复初始化
这是最常见的坑!比如在Web应用的web.xml里,你可能同时配置了ContextLoaderListener和DispatcherServlet,且两者的配置文件都扫描了调度相关的Bean,导致整个调度器被实例化两次,自然会触发两次任务。
解决办法:
- 拆分配置职责:让
ContextLoaderListener负责加载业务Bean、调度Bean这类非MVC组件,DispatcherServlet只负责扫描控制器(Controller)相关的Bean; - 或者在其中一个配置文件里通过
exclude-filter排除调度Bean的扫描,避免重复创建。
2. Job/Trigger被重复注册
检查项目中是否有其他配置文件、或者Java Config代码也注册了同一个runMeJob或cronTrigger,导致调度器中存在两个完全相同的Trigger,到点就会同时触发。
解决办法:
- 全局排查所有Spring配置,确保同一个Job和Trigger只定义一次;
- 给
SchedulerFactoryBean添加overwriteExistingJobs属性为true,重复注册的Job会被覆盖,不会重复触发:<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> <property name="overwriteExistingJobs" value="true" /> </bean>
3. 集群环境下的并发执行
如果应用部署在集群多节点上,默认情况下每个节点的Quartz都会独立触发任务,就会导致同一时间多个节点执行同一个Job。
解决办法:
- 配置Quartz使用JDBC JobStore:把Job和Trigger的状态存储到数据库中,集群节点会通过数据库锁保证只有一个节点执行任务;
- 禁止Job并发执行:给JobDetail设置
concurrent属性为false,避免同一个Job被同时执行:<bean name="runMeJob" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="com.ascent.fieldomobify.cornScheduler.RunMeJob" /> <property name="jobDataAsMap"> <map> <entry key="runMeTask" value-ref="runMeTask" /> </map> </property> <property name="concurrent" value="false" /> </bean>
4. 时区问题导致的重复触发
你的Cron表达式0 0 13 * * ?本身是正确的,但如果Quartz使用的时区和预期不一致(比如夏令时切换),可能会出现一天内触发两次的情况。
解决办法:
- 给
SchedulerFactoryBean明确指定时区,比如国内使用Asia/Shanghai:<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> </list> </property> <property name="timeZone" value="Asia/Shanghai" /> </bean>
内容的提问来源于stack exchange,提问作者Prashant
相关产品推荐
相关产品推荐

