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(); } }