你所不知道的Quartz特性

很多朋友的博文,講到使用Quartz來進行任務調度,很簡單就是告訴你引用一個jar包,實現一個Job接口,然後schedule起來就OK了,此外講的最多的是CronTrigger的使用。給人的假象是任務調度原來真是如此的簡單。

    但是作為一個新手,你可知道Job會佔用多少線程?能否滿足自己調度的性能需要呢?Quartz的線程使用看起來很簡單,配置org.quartz.threadPool.threadCount這個參數,框架會自動為你初始化threadCount個WorkThread,一切省心省力。然而,Job和Thread是否一一對應呢?很明顯不是。當Job數量等於Thread數的時候,其執行是怎樣的情況呢?多於,少於的時候又是怎樣的情況呢?如果Job的執行時間大於了Tigger的時間間隔,執行情況又是怎樣的呢?這纔是任務調度實質需要考慮的內容。

首先我們看一個Job一個線程,這個線程啥也不做,就打印一行日誌,配置threadCount: 1, 只啓動一個Job,可以看到執行情況相當精準就是5秒一次。我們再把Job改爲啓動10個,線程數保持不變,將可以看到執行仍然是精確的5秒一次。

for (int i= 0; i < 1; i ++)
        {
            Date runTime = new Date();
            System.out.print(runTime);
            
            // define the job and tie it to our HelloJob class
            JobDetail job = newJob(HelloJob.class)
                .withIdentity("job" + i, "group1")
                .build();
            

            Trigger trigger = newTrigger()
                .withIdentity("trigger" + i, "group1")
                .startAt(runTime).withSchedule(simpleSchedule()
                        .withIntervalInSeconds(5).repeatForever())
                .build();
            
            // Tell quartz to schedule the job using our trigger
            s1.scheduleJob(job, trigger);
        }

信息: Scheduler TestScheduler_$_instance_one started.
Sun Jun 03 21:23:08 CST 20122012-6-3 21:23:08 org.quartz.examples.example1.HelloJob execute
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:23:08 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
2012-6-3 21:23:13 org.quartz.examples.example1.HelloJob execute
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:23:13 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
2012-6-3 21:23:18 org.quartz.examples.example1.HelloJob execute
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:23:18 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
2012-6-3 21:23:23 org.quartz.examples.example1.HelloJob execute
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:23:23 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13


 

    然而,假設這個Job由於任務繁重,執行時間超過5秒,又是怎麼樣一副光景呢?仍以1個線程1個Job爲例,如果這個Job執行時間爲6秒。那麼第一次開始執行5秒這個時間點的時候(第一次執行尚未完成),quartz會啓動第二次執行嗎?在Job的execute方法中加一個Thread.sleep(6000);我們會發現,Job變成6秒執行一次啦。然而,如果把Job加到100個,又會是怎樣的呢?可以想見由於只有1個線程,所有Job仍然會6秒執行一次,然而這10個Job能夠平均分配到執行機會嗎?也就是說,慢就慢點吧,所有的線程總要有機會run才行啊。事實卻是不行,Job0會每隔6秒執行一次,Job1-99則會永遠等待在內存裏。那麼,如果Job的執行時間改爲2秒呢,這下好了,每個Job倒是都有機會執行到了,但是每個Job能夠執行到的概率絕對不是平均分佈的,如下:

信息: Scheduler TestScheduler_$_instance_one started.
Sun Jun 03 21:48:30 CST
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:30 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:32 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job2@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:34 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:36 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:38 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:40 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:42 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job3@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:44 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:46 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:48 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:50 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:52 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job4@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:54 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:56 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:48:58 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:00 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:02 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job5@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:04 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:06 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:08 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:10 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:12 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job6@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:14 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:16 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:18 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:20 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:22 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job7@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:24 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:26 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:28 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:30 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:32 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job8@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:34 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:36 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:38 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:40 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:42 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job9@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:44 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:46 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:48 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:50 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:52 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job0@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:55 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job1@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:57 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13
信息: Hello World! - @@@@@group1.job2@@@@@@@@@@@@@@@@@@@@@@@@Sun Jun 03 21:49:59 CST 2012@@@@@@@@@@@@@@@@@@@@@@@@13



Job0總是格外受青睞點,傷不起啊~ 

 

事實上,SimpleSchedule對於Job到期不能執行的問題出現以後,有着不同的策略。默認選項是withMisfireHandlingInstructionFireNow,如果Job執行時間(6S)大於Interval(5S),會導致Job0先執行完RepeatCount+1次,然後執行Job2完RepeatCount+1次,以此類推。如果採用withMisfireHandlingInstructionNextWithExistingCount 或者withMisfireHandlingInstructionNextWithRemainingCount選項,很悲劇,執行完repeatCount+1次Job0以後不會再執行其他Job了,因爲Schedule在一開始就計算好了每個Job的FinalTime,過了這個時間就不再執行。選項withMisfireHandlingInstructionNowWithExistingCount則與默認行爲相同(在沒有出現暫停或者恢復調度這類操作的前提下),withMisfireHandlingInstructionNowWithRemainingCount與默認選項類似,又稍有差別。

 

而對於repeatForever的Job,採用上述任何選項,都會導致後面的Job得不到執行,程序會永遠執行前N個Job,N取決於org.quartz.threadPool.threadCount這個參數,就如同本文開頭所示的那樣。而SimpleSchedule還有一個選項withMisfireHandlingInstructionIgnoreMisfires,它會重做所有錯過的週期,這樣一來,10個Job就能均勻分享1個線程了。

 

 最後,還剩一個問題,如果threadCount大於Job數量,而且 Job執行時間(6S)大於Interval(5S),那麼,取決於是否使用@DisallowConcurrentExecution,在Interval到點時,可能會準時(不使用註解)或不會準時(使用註解)再起一個新的線程執行相同的Job。如果您的Job執行時間很長,但又不希望同時有多個相同的Job在並發執行,這個註解就派上用場。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章