quartz本身和spring都提供了设置quartz任务串行的解决办法。
没有用spring的情况:
方法1,task实现StatefulJob接口。这个接口在quartz2.0的时候作废了,现在建议使用注解的方式完成串行的配置,不过这个接口依然有效,因为2.0版本的这个接口使用了@DisallowConcurrentExecution和@PersistJobDataAfterExecution的注解。
方法2,注解@DisallowConcurrentExecution。这个代表设置该任务为串行执行。
另外:注解@PersistJobDataAfterExecution。这个代表该任务可以支持在任务间使用JobDataMap传递信息,在任务结束时保存信息,不必设置为串行。但如果并行任务使用该注解可能会让JobDataMap中的内容产生不可预知的结果,所以还是强烈建议使用该注解的同时使用@DisallowConcurrentExecution注解。
然后任务的定义大概是这样的:
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class BillingBillTask extends Job {
publicstatic int test=1;
publicvoid executeInternal()throws JobExecutionException {
test++;
JobDataMapmap=arg0.getJobDetail().getJobDataMap();
if(map.get("key")==null){
System.out.println("key==null");
map.put("key","123");
}else{
System.out.println("key!=null");
System.out.println(map.get("key"));
}
}
}
用了spring和quartz结合的情况:
方法1,使用前文没用spring的方法,依然适用。
方法2,spring-quartz.xml配置文件修改。
spring提供了几个使用quartz的类:
第一个类:JobDetailFactoryBean。
使用这个类,配置文件大概是这样的:
<bean id="jobDetail7" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<propertyname="jobClass"value="com.dangdang.tms.job.schedule.bms.BillingBillTask"></property>
<propertyname="durability" value="true" />
</bean>
<beanid="cronTriggerBean7" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<propertyname="jobDetail" ref="jobDetail7"></property>
<propertyname="cronExpression" value="0/5 * * * * ?"></property>
</bean>
task的定义大概是这样的:
public class BillingBillTask extends QuartzJobBean{
publicstatic int test=1;
@Override
publicvoid executeInternal(JobExecutionContext arg0)throws JobExecutionException {
test++;
JobDataMap map=arg0.getJobDetail().getJobDataMap();
if(map.get("key")==null){
System.out.println("key==null");
map.put("key","123");
}else{
System.out.println("key!=null");
System.out.println(map.get("key"));
}
}
}
注:
1, task类需要继承QuartzJobBean。
2, 这个类在spring中没有提供让quartz作业串行执行的配置。如果用了这个类还要串行执行的话,使用@DisallowConcurrentExecution注解吧。
3, 如果设置了串行执行,JobDetail中的JobDataMap可以在多次执行的任务中传递内容,不会因为任务结束而清空。
第二个类:MethodInvokingJobDetailFactoryBean。
使用这个类,配置文件大概是这样的:
<bean id="jobDetail7"class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<propertyname="targetObject" ref="billingBillTask"></property>
<propertyname="targetMethod" value="executeInternal" />
<propertyname="concurrent" value="false" />
</bean>
<beanid="billingBillTask"class="com.dangdang.tms.job.schedule.bms.BillingBillTask"/>
<beanid="cronTriggerBean7" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
<propertyname="jobDetail" ref="jobDetail7"></property>
<propertyname="cronExpression" value="0/5 * * * * ?"></property>
</bean>
task的定义大概是这样的:
public class BillingBillTask{
public static int test=1;
publicvoid executeInternal()throws JobExecutionException {
test++;
}
}
注:
1,<propertyname="targetObject"ref="billingBillTask"></property>中的ref不能直接写成value=”com.dangdang.tms.job.schedule.bms.BillingBillTask”这种形式,必须得下面再写个id=”billingBillTask”,原因没研究过。
2,这种配置中设置的executeInternal方法必须是无参数的,如果设置了参数就会报找不到方法的异常。
4, 这种配置中,即使设置了串行执行,无法使用JobDataMap来在串行的任务中传递参数,如果需要传递参数的话,另想办法。比如定义一个静态变量之类的。
有misfireThreshold参数,定义过期时间。
quartz中默认的misfireThreshold是60000,也就是60秒。
我对这个参数的理解是,一个在串行的时候后面任务被阻碍多久就放弃的参数,比如:
设置该参数为10秒,定时任务串行执行,20秒执行一次。
如果第一个任务0秒开始执行,执行到第25秒结束,第二个定时任务应该20s时开始执行,但拖到了25s,与执行时间间隔了5秒,小于配置的10秒,那么第二个定时任务会在第25s,也就是第一个定时任务结束的当时就开始执行。
如果第一个任务0秒开始执行,执行到第35秒结束,第二个定时任务应该20s开始执行,但拖到了35秒,与执行时间间隔了15秒,大于配置的10秒,那么20s开始执行的任务会放弃,而按照配置中的下次执行时间也就是第40s开始执行。
这个参数在quartz中的配置是这样的:
org.quartz.jobStore.misfireThreshold: 60000
在spring中的配置是这样的:(试了一下没有成功,还是按照60秒放弃任务,原因未知)
<bean id="trigger" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<propertyname="triggers">
<list>
<refbean="cronTriggerBean7" />
</list>
</property>
<propertyname="jobFactory">
<beanclass="com.dangdang.tms.job.schedule.AutowiredJobFactory"></bean>
</property>
<property name="applicationContextSchedulerContextKey"value="applicationContextKey" />
<property name="quartzProperties">
<props>
<propkey="org.quartz.jobStore.misfireThreshold">10000</prop>
</props>
</property>
</bean>
另外,对于因为串行而错过执行时间的任务,quartz有多种策略,比如:
withMisfireHandlingInstructionDoNothing:不立即触发,等下次执行时间执行
withMisfireHandlingInstructionFireAndProceed:立即触发执行
还有很多其他规则奇葩看起来十分眼晕的策略,没深入研究。