Quartz 并发/单线程
Quartz定时任务默认都是并发执行的,不会等待上一次任务执行完毕,只要间隔时间到就会执行, 如果定时任执行太长,会长时间占用资源,导致其它任务堵塞。
1.在Spring中这时需要设置concurrent的值为false, 禁止并发执行。
<property name="concurrent" value="true" />
2.当不使用spring的时候就需要在Job的实现类上加@DisallowConcurrentExecution的注释
@DisallowConcurrentExecution 禁止并发执行多个相同定义的JobDetail, 这个注解是加在Job类上的, 但意思并不是不能同时执行多个Job, 而是不能并发执行同一个Job Definition(由JobDetail定义), 但是可以同时执行多个不同的JobDetail, 举例说明,我们有一个Job类,叫做SayHelloJob, 并在这个Job上加了这个注解, 然后在这个Job上定义了很多个JobDetail, 如sayHelloToJoeJobDetail, sayHelloToMikeJobDetail, 那么当scheduler启动时, 不会并发执行多个sayHelloToJoeJobDetail或者sayHelloToMikeJobDetail, 但可以同时执行sayHelloToJoeJobDetail跟sayHelloToMikeJobDetail
@PersistJobDataAfterExecution 同样, 也是加在Job上,表示当正常执行完Job后, JobDataMap中的数据应该被改动, 以被下一次调用时用。当使用@PersistJobDataAfterExecution 注解时, 为了避免并发时, 存储数据造成混乱, 强烈建议把@DisallowConcurrentExecution注解也加上。
@DisallowConcurrentExecution
此标记用在实现Job的类上面,意思是不允许并发执行,按照我之前的理解是 不允许调度框架在同一时刻调用Job类,后来经过测试发现并不是这样,而是Job(任务)的执行时间[比如需要10秒]大于任务的时间间隔[Interval(5秒)],那么默认情况下,调度框架为了能让 任务按照我们预定的时间间隔执行,会马上启用新的线程执行任务。否则的话会等待任务执行完毕以后 再重新执行!(这样会导致任务的执行不是按照我们预先定义的时间间隔执行)
测试代码,这是官方提供的例子。设定的时间间隔为3秒,但job执行时间是5秒,设置@DisallowConcurrentExecution以后程序会等任务执行完毕以后再去执行,否则会在3秒时再启用新的线程执行
org.quartz.threadPool.threadCount = 5 这里配置框架的线程池中线程的数量,要多配置几个,否则@DisallowConcurrentExecution不起作用
org.quartz.scheduler.instanceName = MyScheduler org.quartz.threadPool.threadCount = 5 org.quartz.jobStore.class =org.quartz.simpl.RAMJobStore
/* * Copyright 2005 - 2009 Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package org.quartz.examples.example5; import java.util.Date; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.PersistJobDataAfterExecution; /** * <p> A dumb implementation of Job, for unit testing purposes. </p> * * @author James House */ @PersistJobDataAfterExecution @DisallowConcurrentExecution public class StatefulDumbJob implements Job { /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Constants. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ public static final String NUM_EXECUTIONS = "NumExecutions"; public static final String EXECUTION_DELAY = "ExecutionDelay"; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Constructors. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ public StatefulDumbJob() { } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * Interface. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /** * <p> Called by the <code>{@link org.quartz.Scheduler}</code> when a <code>{@link org.quartz.Trigger}</code> fires that is associated with the <code>Job</code>. </p> * * @throws JobExecutionException if there is an exception while executing the job. */ public void execute(JobExecutionContext context) throws JobExecutionException { System.err.println("---" + context.getJobDetail().getKey() + " executing.[" + new Date() + "]"); JobDataMap map = context.getJobDetail().getJobDataMap(); int executeCount = 0; if (map.containsKey(NUM_EXECUTIONS)) { executeCount = map.getInt(NUM_EXECUTIONS); } executeCount++; map.put(NUM_EXECUTIONS, executeCount); long delay = 5000l; if (map.containsKey(EXECUTION_DELAY)) { delay = map.getLong(EXECUTION_DELAY); } try { Thread.sleep(delay); } catch (Exception ignore) { } System.err.println(" -" + context.getJobDetail().getKey() + " complete (" + executeCount + ")."); } }
/* * Copyright 2005 - 2009 Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package org.quartz.examples.example5; import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; import static org.quartz.DateBuilder.*; import java.util.Date; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.SchedulerMetaData; import org.quartz.SimpleTrigger; import org.quartz.impl.StdSchedulerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Demonstrates the behavior of <code>StatefulJob</code>s, as well as how misfire instructions affect the firings of triggers of <code>StatefulJob</code> s - when the jobs take longer to execute that the frequency of the trigger's repitition. * * <p> While the example is running, you should note that there are two triggers with identical schedules, firing identical jobs. The triggers "want" to fire every 3 seconds, but the jobs take 10 seconds to execute. Therefore, by the time the jobs complete their execution, the triggers have already "misfired" (unless the scheduler's "misfire threshold" has been set to more than 7 seconds). You should see that one of the jobs has its misfire instruction set to <code>SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT</code>, which causes it to fire immediately, when the misfire is detected. The other trigger uses the default "smart policy" misfire instruction, which causes the trigger to advance to its next fire time (skipping those that it has missed) - so that it does not refire immediately, but rather at the next scheduled time. </p> * * @author <a href="mailto:bonhamcm@thirdeyeconsulting.com">Chris Bonham</a> */ public class MisfireExample { public void run() throws Exception { Logger log = LoggerFactory.getLogger(MisfireExample.class); log.info("------- Initializing -------------------"); // First we must get a reference to a scheduler SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); log.info("------- Initialization Complete -----------"); log.info("------- Scheduling Jobs -----------"); // jobs can be scheduled before start() has been called // get a "nice round" time a few seconds in the future... Date startTime = nextGivenSecondDate(null, 15); // statefulJob1 will run every three seconds // (but it will delay for ten seconds) JobDetail job = newJob(StatefulDumbJob.class).withIdentity("statefulJob1", "group1").usingJobData(StatefulDumbJob.EXECUTION_DELAY, 10000L).build(); SimpleTrigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(3).repeatForever()).build(); Date ft = sched.scheduleJob(job, trigger); log.info(job.getKey() + " will run at: " + ft + " and repeat: " + trigger.getRepeatCount() + " times, every " + trigger.getRepeatInterval() / 1000 + " seconds"); log.info("------- Starting Scheduler ----------------"); // jobs don't start firing until start() has been called... sched.start(); log.info("------- Started Scheduler -----------------"); try { // sleep for ten minutes for triggers to file.... Thread.sleep(600L * 1000L); } catch (Exception e) { } log.info("------- Shutting Down ---------------------"); sched.shutdown(true); log.info("------- Shutdown Complete -----------------"); SchedulerMetaData metaData = sched.getMetaData(); log.info("Executed " + metaData.getNumberOfJobsExecuted() + " jobs."); } public static void main(String[] args) throws Exception { MisfireExample example = new MisfireExample(); example.run(); } }
@PersistJobDataAfterExecution
此标记说明在执行完Job的execution方法后保存JobDataMap当中固定数据,在默认情况下 也就是没有设置 @PersistJobDataAfterExecution的时候 每个job都拥有独立JobDataMap
否则改任务在重复执行的时候具有相同的JobDataMap
/* * Copyright 2005 - 2009 Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package com.quartz.demo.example6; import java.util.Date; import org.quartz.DisallowConcurrentExecution; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.JobKey; import org.quartz.PersistJobDataAfterExecution; @PersistJobDataAfterExecution @DisallowConcurrentExecution public class BadJob1 implements Job { public BadJob1() { } public void execute(JobExecutionContext context) throws JobExecutionException { JobKey jobKey = context.getJobDetail().getKey(); JobDataMap dataMap = context.getJobDetail().getJobDataMap(); int denominator = dataMap.getInt("denominator"); System.out.println("---" + jobKey + " executing at " + new Date() + " with denominator " + denominator); denominator++; dataMap.put("denominator", denominator); } }
/* * Copyright 2005 - 2009 Terracotta, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy * of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * */ package com.quartz.demo.example6; import static org.quartz.JobBuilder.newJob; import static org.quartz.SimpleScheduleBuilder.simpleSchedule; import static org.quartz.TriggerBuilder.newTrigger; import static org.quartz.DateBuilder.*; import java.util.Date; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerFactory; import org.quartz.SimpleTrigger; import org.quartz.impl.StdSchedulerFactory; public class JobExceptionExample { public void run() throws Exception { // First we must get a reference to a scheduler SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); // jobs can be scheduled before start() has been called // get a "nice round" time a few seconds in the future... Date startTime = nextGivenSecondDate(null, 2); JobDetail job = newJob(BadJob1.class).withIdentity("badJob1", "group1").usingJobData("denominator", "0").build(); SimpleTrigger trigger = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(2).repeatForever()).build(); Date ft = sched.scheduleJob(job, trigger); //任务每2秒执行一次 那么在BadJob1的方法中拿到的JobDataMap的数据是共享的. //这里要注意一个情况: 就是JobDataMap的数据共享只针对一个BadJob1任务。 //如果在下面在新增加一个任务 那么他们之间是不共享的 比如下面 JobDetail job2 = newJob(BadJob1.class).withIdentity("badJob1", "group1").usingJobData("denominator", "0").build(); SimpleTrigger trigger2 = newTrigger().withIdentity("trigger1", "group1").startAt(startTime).withSchedule(simpleSchedule().withIntervalInSeconds(2).repeatForever()).build(); //这个job2与job执行的JobDataMap不共享 sched.scheduleJob(job2, trigger2); sched.start(); try { // sleep for 30 seconds Thread.sleep(30L * 1000L); } catch (Exception e) { } sched.shutdown(false); } public static void main(String[] args) throws Exception { JobExceptionExample example = new JobExceptionExample(); example.run(); } }