使用Quartz做计划任务时,默认情况下,当前任务总会执行,无论前一个任务是否结束。
使用Spring配置Quartz的Job的时候,也是默认当前任务总会执行,无论前一个任务是否结束。
因此,如果采用默认条件,需要考虑并发执行的逻辑问题,否则需要设置为顺序执行。
我写了一个Spring配置Quartz的Demo。里面Spring的xml中
代码如下:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:jms="http://www.springframework.org/schema/jms" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:oxm="http://www.springframework.org/schema/oxm" xmlns:p="http://www.springframework.org/schema/p" xmlns:sec="http://www.springframework.org/schema/security" xmlns:task="http://www.springframework.org/schema/task" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-4.0.xsd http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.0.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-4.0.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.0.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-4.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd"> <bean id="testJob" class="org.kanpiaoxue.learn.quartz.disallowConcurrentExecution.TestJob" /> <bean id="testJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="testJob" /> <property name="targetMethod" value="execute" /> <!-- 使用Quartz做计划任务时,默认情况下,当前任务总会执行,无论前一个任务是否结束。 设置 <property name="concurrent" value="false" /> 可以让Job顺序执行。 设置 <property name="concurrent" value="true" /> 可以让Job并行执行,而不用管上一个Job是否结束。 --> <property name="concurrent" value="false" /> </bean> <bean id="testJobCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="testJobDetail" /> <!-- 间隔1秒钟执行一次 --> <property name="cronExpression" value="0/1 * * * * ? *" /> </bean> <bean id="taskExecutor" class="org.kanpiaoxue.learn.quartz.disallowConcurrentExecution.ThreadPoolExecutorImpl"> <constructor-arg name="nThreads" value="30" /> </bean> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="taskExecutor" ref="taskExecutor" /> <property name="triggers"> <list> <ref bean="testJobCronTrigger" /> </list> </property> </bean> <!-- job end --> </beans>
Main函数的入口类如下:
import org.springframework.context.support.FileSystemXmlApplicationContext; /** * <pre> * Test.java * @author kanpiaoxue<br> * @version 1.0 * Create Time 2014年8月12日 下午6:45:06<br> * Description : main函数入口 * </pre> */ public class Test { /** * <pre> * @param args * </pre> */ @SuppressWarnings("resource") public static void main(String[] args) { new FileSystemXmlApplicationContext( "D:/org/kanpiaoxue/learn/quartz/disallowConcurrentExecution/spring-ctx-application.xml"); } }
Job的测试类如下:
import org.joda.time.DateTime; import java.util.concurrent.TimeUnit; /** * <pre> * TestJob.java * @author kanpiaoxue<br> * @version 1.0 * Create Time 2014年8月12日 下午6:45:48<br> * Description : Job测试类 * </pre> */ public class TestJob { public TestJob() { } public void execute() throws InterruptedException { System.out.println(String.format("[%s] %s start to work", getNow(), Thread.currentThread().getName())); /** * 让Job沉睡20秒,模拟当前Job一直在执行任务的情形。 */ TimeUnit.SECONDS.sleep(20L); System.out.println(String.format("[%s] %s wake up.", getNow(), Thread .currentThread().getName())); } private String getNow() { return DateTime.now().toString("yyyy-MM-dd HH:mm:ss"); } }
如果不设置Spring的Quartz执行的线程大小,默认是:10个
我们也可以指定线程池的大小,代码如下:
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * <pre> * ThreadPoolExecutorImpl.java * @author kanpiaoxue<br> * @version 1.0 * Create Time 2014年8月12日 下午7:04:44<br> * Description : 线程池类 * </pre> */ public class ThreadPoolExecutorImpl extends ThreadPoolExecutor { /** * <pre> * @param nThreads 线程池的大小 * </pre> */ public ThreadPoolExecutorImpl(int nThreads) { super(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); } }
通过在Spring的XML中配置 org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean 的 concurrent 的属性,可以决定Quartz的Job是否顺序执行,还是不考虑顺序的并行执行。
查看 MethodInvokingJobDetailFactoryBean 关于 concurrent 属性的原码,可以发现默认是“并行”的。
MethodInvokingJobDetailFactoryBean 的部分原码:
public class MethodInvokingJobDetailFactoryBean extends ArgumentConvertingMethodInvoker implements FactoryBean<JobDetail>, BeanNameAware, BeanClassLoaderAware, BeanFactoryAware, InitializingBean { private static Class<?> jobDetailImplClass; private static Method setResultMethod; static { try { jobDetailImplClass = Class.forName("org.quartz.impl.JobDetailImpl"); } catch (ClassNotFoundException ex) { jobDetailImplClass = null; } try { Class<?> jobExecutionContextClass = QuartzJobBean.class.getClassLoader().loadClass("org.quartz.JobExecutionContext"); setResultMethod = jobExecutionContextClass.getMethod("setResult", Object.class); } catch (Exception ex) { throw new IllegalStateException("Incompatible Quartz API: " + ex); } } private String name; private String group = Scheduler.DEFAULT_GROUP; private boolean concurrent = true;
关于Quartz原生的Job的顺序执行,请参考文章: http://feuyeux.iteye.com/blog/1842966