摘要:注意當使用注解時,為了避免并發時,存儲數據造成混亂,強烈建議把注解也加上。示例假設定時任務的時間間隔為秒,但執行時間是秒。當設置以后程序會等任務執行完畢后再去執行,否則會在秒時再啟動新的線程執行。
1. 禁止同一個 JobDetail 中的多個實例并發執行版權聲明:本文由吳仙杰創作整理,轉載請注明出處:https://segmentfault.com/a/1190000009128328
Quartz 定時任務默認都是并發執行的,不會等待上一次任務執行完畢,只要間隔時間到就會執行,如果定時任執行太長,會長時間占用資源,導致其它任務堵塞。
禁止并發執行的意思并不是不能同時執行多個 Job,而是不能并發執行同一個 Job Definition(由 JobDetail 定義),但是可以同時執行多個不同的 JobDetail,舉例說明,我們有一個 Job 類,叫做 SayHelloJob,并在這個 Job 上加了 @DisallowConcurrentExecution 注解,然后在這個 Job上 定義了很多個 JobDetail,如 sayHelloToJoeJobDetail,sayHelloToMikeJobDetail,那么當 scheduler 啟動時,不會并發執行多個 sayHelloToJoeJobDetail 或者 sayHelloToMikeJobDetail,但可以同時執行 sayHelloToJoeJobDetail 跟 sayHelloToMikeJobDetail。
1.1 使用 Spring 整合 Quartz 時Spring 配置文件中加入:
1.2 Quartz 原生使用時
當不使用 Spring 的時候就需要在 Job 的實現類上加 @DisallowConcurrentExecution 的注解。
2. 同一個 JobDetail 中多個實例的數據共享@PersistJobDataAfterExecution 是用在 Job 實現類上,表示一個有狀態的任務,意思是當正常執行完 Job 后,JobDataMap 中的數據應該被改動,以被下一次調用時用。
注意:當使用 @PersistJobDataAfterExecution 注解時,為了避免并發時,存儲數據造成混亂,強烈建議把 @DisallowConcurrentExecution 注解也加上。
3. 示例假設定時任務的時間間隔為 3 秒,但 job 執行時間是 10 秒。當設置 @DisallowConcurrentExecution 以后程序會等任務執行完畢后再去執行,否則會在 3 秒時再啟動新的線程執行。
當設置 @PersistJobDataAfterExecution 時,在執行完 Job 的 execution 方法后保存 JobDataMap 當中固定數據,以便任務在重復執行的時候具有相同的 JobDataMap;在默認情況下也就是沒有設置 @PersistJobDataAfterExecution 的時候每個 job 都擁有獨立 JobDataMap。
任務類:
package org.quartz.examples; import org.quartz.*; import java.util.Date; @PersistJobDataAfterExecution @DisallowConcurrentExecution public class TaskJob implements Job { public static final String NUM_EXECUTIONS = "NumExecutions"; public static final String EXECUTION_DELAY = "ExecutionDelay"; /** * 靜態變量可以保持工作狀態,但無法達到預期效果 */ private static int _staticCounter = 0; /** * Quartz 每次執行作業時都會重新實例化,非靜態變量無法保持工作狀態 */ private int _counter = 0; /** * 需要一個公共的空構造方法,以便 scheduler 隨時實例化 job */ public TaskJob() { } /** * 該方法實現需要執行的任務 */ public void execute(JobExecutionContext context) throws JobExecutionException { System.err.println("---> " + context.getJobDetail().getKey() + " 運行中[" + new Date() + "]"); JobDataMap map = context.getJobDetail().getJobDataMap(); int executeCount = 0; if (map.containsKey(NUM_EXECUTIONS)) { executeCount = map.getInt(NUM_EXECUTIONS); } // 增量計數并將其存儲回 JobDataMap,這樣可以適當保持工作狀態 executeCount++; map.put(NUM_EXECUTIONS, executeCount); // 只要有任務執行都會遞增,無法達到預期效果 _staticCounter++; // 本地變量遞增加,但實際上無法保持工作狀態 _counter++; long delay = 5000L; if (map.containsKey(EXECUTION_DELAY)) { delay = map.getLong(EXECUTION_DELAY); } try { // 模擬一個耗時的 job Thread.sleep(delay); } catch (InterruptedException e) { e.printStackTrace(); } System.err.println(context.getJobDetail().getKey() + " 的靜態變量 _staticCounter 為:" + _staticCounter + ",非靜態變量 scheduler 為:" + _counter); System.err.println(context.getJobDetail().getKey() + " 完成了(" + executeCount + ")次 <---"); } }
任務調度類:
package org.quartz.examples; import org.quartz.*; import org.quartz.impl.StdSchedulerFactory; import java.util.Date; public class Executer { public void run() throws Exception { // 通過 schedulerFactory 獲取一個調度器 SchedulerFactory sf = new StdSchedulerFactory(); Scheduler sched = sf.getScheduler(); // 創建 jobDetail 實例,綁定 Job 實現類 // 指明 job 的名稱,所在組的名稱,以及綁定 job 類 JobDetail job1 = JobBuilder.newJob(TaskJob.class) .withIdentity("statefulJob1", "group1") // 給定的鍵-值對添加到 JobDetail 的 JobDataMap 中 .usingJobData(TaskJob.EXECUTION_DELAY, 10000L).build(); // 定義調度觸發規則,先立即執行一次,然后每隔 3 秒執行一次 SimpleTrigger trigger = TriggerBuilder.newTrigger() .withIdentity("trigger1", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule() .withIntervalInSeconds(3) .repeatForever()) .build(); // 把作業和觸發器注冊到任務調度中 Date firstRunTime = sched.scheduleJob(job1, trigger); System.out.println(job1.getKey() + " 開始運行于:" + firstRunTime + ",重復:" + trigger.getRepeatCount() + " 次,每次間隔 " + trigger.getRepeatInterval() / 1000 + " 秒"); // 任務 job1 方法中拿到的 JobDataMap 的數據是共享的 // 這里要注意一個情況: 就是 JobDataMap 的數據共享只針對一個 job1 任務 // 如果在下面在新增加一個任務 那么他們之間是不共享的,比如下面的 job2 // 創建第二個 JobDetail 實例 JobDetail job2 = JobBuilder.newJob(TaskJob.class) .withIdentity("statefulJob2", "group1") // 給定的鍵-值對添加到 JobDetail 的 JobDataMap 中 .usingJobData(TaskJob.EXECUTION_DELAY, 10000L) .build(); // 定義調度觸發規則,先立即執行一次,然后每隔 3 秒執行一次 trigger = TriggerBuilder.newTrigger() .withIdentity("trigger2", "group1") .startNow() .withSchedule(SimpleScheduleBuilder.simpleSchedule(). withIntervalInSeconds(3) .repeatForever() // 指定失效時的策略 .withMisfireHandlingInstructionNowWithExistingCount()) .build(); // 這個 job2 與 job1 執行的 JobDataMap 不共享 // 把作業和觸發器注冊到任務調度中 firstRunTime = sched.scheduleJob(job2, trigger); System.out.println(job2.getKey() + " 開始運行于:" + firstRunTime + ",重復:" + trigger.getRepeatCount() + " 次,每次間隔 " + trigger.getRepeatInterval() / 1000 + " 秒"); // 啟動計劃程序(實際上直到調度器已經啟動才會開始運行) sched.start(); // 等待 60 秒,使我們的 job 有機會執行 Thread.sleep(60000); // 等待作業執行完成時才關閉調度器 sched.shutdown(true); SchedulerMetaData metaData = sched.getMetaData(); System.out.println("一共運行了:" + metaData.getNumberOfJobsExecuted() + " 個任務"); } public static void main(String[] args) throws Exception { Executer example = new Executer(); example.run(); } }
運行結果:
group1.statefulJob1 開始運行于:Wed Apr 19 17:04:22 CST 2017,重復:-1 次,每次間隔 3 秒 group1.statefulJob2 開始運行于:Wed Apr 19 17:04:22 CST 2017,重復:-1 次,每次間隔 3 秒 ---> group1.statefulJob2 運行中[Wed Apr 19 17:04:22 CST 2017] ---> group1.statefulJob1 運行中[Wed Apr 19 17:04:22 CST 2017] group1.statefulJob2 的靜態變量 _staticCounter 為:2,非靜態變量 scheduler 為:1 group1.statefulJob1 的靜態變量 _staticCounter 為:2,非靜態變量 scheduler 為:1 group1.statefulJob2 完成了(1)次 <--- group1.statefulJob1 完成了(1)次 <--- ---> group1.statefulJob1 運行中[Wed Apr 19 17:04:32 CST 2017] ---> group1.statefulJob2 運行中[Wed Apr 19 17:04:32 CST 2017] group1.statefulJob1 的靜態變量 _staticCounter 為:4,非靜態變量 scheduler 為:1 group1.statefulJob1 完成了(2)次 <--- group1.statefulJob2 的靜態變量 _staticCounter 為:4,非靜態變量 scheduler 為:1 group1.statefulJob2 完成了(2)次 <--- ---> group1.statefulJob1 運行中[Wed Apr 19 17:04:42 CST 2017] ---> group1.statefulJob2 運行中[Wed Apr 19 17:04:42 CST 2017] group1.statefulJob2 的靜態變量 _staticCounter 為:6,非靜態變量 scheduler 為:1 group1.statefulJob1 的靜態變量 _staticCounter 為:6,非靜態變量 scheduler 為:1 group1.statefulJob1 完成了(3)次 <--- group1.statefulJob2 完成了(3)次 <--- ---> group1.statefulJob1 運行中[Wed Apr 19 17:04:52 CST 2017] ---> group1.statefulJob2 運行中[Wed Apr 19 17:04:52 CST 2017] group1.statefulJob2 的靜態變量 _staticCounter 為:8,非靜態變量 scheduler 為:1 group1.statefulJob2 完成了(4)次 <--- group1.statefulJob1 的靜態變量 _staticCounter 為:8,非靜態變量 scheduler 為:1 group1.statefulJob1 完成了(4)次 <--- ---> group1.statefulJob2 運行中[Wed Apr 19 17:05:02 CST 2017] ---> group1.statefulJob1 運行中[Wed Apr 19 17:05:02 CST 2017] group1.statefulJob2 的靜態變量 _staticCounter 為:10,非靜態變量 scheduler 為:1 group1.statefulJob1 的靜態變量 _staticCounter 為:10,非靜態變量 scheduler 為:1 group1.statefulJob2 完成了(5)次 <--- group1.statefulJob1 完成了(5)次 <--- ---> group1.statefulJob1 運行中[Wed Apr 19 17:05:12 CST 2017] ---> group1.statefulJob2 運行中[Wed Apr 19 17:05:12 CST 2017] group1.statefulJob2 的靜態變量 _staticCounter 為:12,非靜態變量 scheduler 為:1 group1.statefulJob2 完成了(6)次 <--- group1.statefulJob1 的靜態變量 _staticCounter 為:12,非靜態變量 scheduler 為:1 group1.statefulJob1 完成了(6)次 <--- 一共運行了:12 個任務4. 參考
Quartz 線程處理
Quartz API
PS:本文針對的 Quartz 版本為 Quartz 2.2.3。官方下載地址:Quartz 2.2.3 .tar.gz
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/67057.html
摘要:注意為了共享在同一個中的,我們需要在上面這個實現類上加入和注解,詳見定時任務二多線程并發執行與數據共享。捕獲異常,取消所有觸發器在我們捕獲異常時,可以調用取消所有與這個作業有關的觸發器。 版權聲明:本文由吳仙杰創作整理,轉載請注明出處:https://segmentfault.com/a/1190000009141079 1. 作業異常 org.quartz.JobExecut...
摘要:調度器就相當于一個容器,裝載著任務和觸發器。用于指定額外的值。然而,如果指定并且第一號是星期六,那么觸發器的觸發在第三號周一,因為它不會過一個月的日子的邊界。注意如果只是指定,則觸發器在月份中不會觸發。 版權聲明:本文由吳仙杰創作整理,轉載請注明出處:https://segmentfault.com/a/1190000009128277 1. Quartz 體系結構 Quartz 設計...
摘要:也是自帶的一個基于線程池設計的定時任務類。其每個調度任務都會分配到線程池中的一個線程執行,所以其任務是并發執行的,互不影響。 原創不易,如需轉載,請注明出處https://www.cnblogs.com/baixianlong/p/10659045.html,否則將追究法律責任!!! 一、在JAVA開發領域,目前可以通過以下幾種方式進行定時任務 1、單機部署模式 Timer:jdk中...
摘要:也有,觸發點和相關,和我們的需求關系不大,暫忽略。實現為每個算法任務創建一個,任務失敗不能啟動后續任務,所以在運行失敗的情況下,需要把啟動的刪除掉。需要自己在中實現多個依賴是否完成的檢查。后續主線程的任務就是檢查工作流是否已經完成。 Quartz簡介 作為一個優秀的開源調度框架,Quartz 具有以下特點:強大的調度功能,支持立即調度、定時調度、周期調度、并發調度; 靈活的應用方式,支...
摘要:三實踐案例案例簡介分布式系統中,微服務基礎組件等,系統中間件,等,對常用功能配置等,進行二次淺封裝并統一集成管理,以滿足日常開發中基礎環境搭建與臨時工具的快速實現。 一、背景簡介 分布式系統中存在很多拆分的服務,在不斷迭代升級的過程中,會出現如下常見的棘手情況: 某個技術組件版本升級,依賴包升級導致部分語法或者API過期,或者組件修復緊急的問題,從而會導致分布式系統下各個服...
閱讀 2672·2021-11-18 10:02
閱讀 3402·2021-09-28 09:35
閱讀 2586·2021-09-22 15:12
閱讀 742·2021-09-22 15:08
閱讀 3071·2021-09-07 09:58
閱讀 3464·2021-08-23 09:42
閱讀 725·2019-08-30 12:53
閱讀 2072·2019-08-29 13:51