现在的位置: 首页 > 综合 > 正文

你所不知道的Quartz特性

2012年08月18日 ⁄ 综合 ⁄ 共 8434字 ⁄ 字号 评论关闭

很多朋友的博文,講到使用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秒一次。

View Code

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能够执行到的概率绝对不是平均分布的,如下:

View Code

信息: 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在并發執行,這個註解就派上用場。

抱歉!评论已关闭.