spring4定时器 cronTrigger和simpleTrigger实现方法
Quartz 是个开源的作业调度框架,为在 Java 应用程序中进行作业调度提供了简单却强大的机制。Quartz 允许开发人员根据时间间隔(或天)来调度作业。它实现了作业和触发器的多对多关系,还能把多个作业与不同的触发器关联。整合了 Quartz 的应用程序可以重用来自不同事件的作业,还可以为一个事件组合多个作业。
SimpleTrigger 当需要在规定的时间执行一次或在规定的时间段以一定的时间间隔重复触发执行Job时,SimpleTrigger就可以满足要求;SimpleTrigger的属性有:开始时间、结束时间、重复次数和重复的时间间隔,重复次数属性的值可以为0、正整数、或常量 SimpleTrigger.REPEAT_INDEFINITELY,重复的时间间隔属性值必须为0或长整型的正整数,以毫秒作为时间单位,当重复的时 间间隔为0时,意味着与Trigger同时触发执行(或几乎与Scheduler开始时同时触发执行)。如果有指定结束时间属性值,则结束时间属性优先于重复次数属性,这样的好处在于:当我们需要创建一个每间隔10秒钟触发一次直到指定的结束时间的 Trigger,而无需去计算从开始到结束的所重复的次数,我们只需简单的指定结束时间和使用REPEAT_INDEFINITELY作为重复次数的属性 值即可(我们也可以指定一个比在指定结束时间到达时实际执行次数大的重复次数)。
CronTrigger 支持比 SimpleTrigger 更具体的调度,而且也不是很复杂。基于 cron 表达式,CronTrigger 支持类似日历的重复间隔,而不是单一的时间间隔。
Cron 表达式包括以下 7 个字段:
格式: [秒] [分] [小时] [日] [月] [周] [年]
序号 说明 是否必填 允许填写的值 允许的通配符
1 秒 是 0-59 , - * /
2 分 是 0-59 , - * /
3 小时 是 0-23 , - * /
4 日 是 1-31 , - * ? / L W
5 月 是 1-12 or JAN-DEC , - * /
6 周 是 1-7 or SUN-SAT , - * ? / L #
7 年 否 empty 或 1970-2099 , - * /
Quartz官方网站对SimpleTrigger和CronTrigger的简单对比:
SimpleTrigger is handy if you need 'one-shot' execution (just single execution of a job at a given moment in time), or if you need to fire a job at a given time, and have it repeat N times, with a delay of T between executions.当你需要的是一次性的调度(仅是安排单独的任务在指定的时间及时执行),或者你需要在指定的时间激活某个任务并执行N次,设置每次任务执行的间隔时间T。那此时使用SimpleTrigger将是非常方便的。
CronTrigger is useful if you wish to have triggering based on calendar-like schedules - such as "every Friday, at noon" or "at 10:15 on the 10th day of every month."如果你需要安排的任务时基于日期的-比如"每个星期五正午"或者"每个月10号的10:15",使用CronTrigger将是非常有用的。
1、配置applicationcontext.xml
<!-- cronTrigger实现方式 --> <bean name="exampleJob" class="org.springframework.scheduling.quartz.JobDetailBean"> <property name="jobClass" value="com.ouku.entities.report.ReportTimerTask" /> <property name="jobDataAsMap"> <map> <entry key="timeout" value="3600" /> </map> </property> </bean> <bean id="springUtil" class="com.ouku.util.SpringUtil" /> <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <property name="jobDetail" ref="exampleJob" /> <!-- run every morning at 6 AM --> <!-- <property name="cronExpression" value="0 0 6 * * ?" /> --> <!-- <property name="cronExpression" value="0 0/1 * * * ?" /> --><!-- 每分钟 --> <property name="cronExpression" value="0/5 * * * * ?" /> <!-- 每秒 --> </bean> <bean id="exampleBusinessObject" class="com.ouku.entities.report.ReportTimerTaskTwo" /> <bean id="jobDetail" <!-- simpleTrigger实现方式 --> class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="exampleBusinessObject" /> <property name="targetMethod" value="doIt" /> <property name="concurrent" value="false" /> </bean> <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean"> <!-- see the example of method invoking job above --> <property name="jobDetail" ref="jobDetail" /> <!-- 0 seconds --> <property name="startDelay" value="0" /> <!-- repeat every 5 seconds --> <property name="repeatInterval" value="5000" /> </bean> <!-- 总调度用于启动Spring定时器 --> <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="cronTrigger" /> <ref bean="simpleTrigger"/> </list> </property> </bean>
其中<property name="concurrent" value="false" />
- 设置为false时,定时任务会串行执行;就是定时任务开启时,知道这个job结束,才会执行下一个定时任务;
- 设置为true时,定时任务会并发执行,就是不管这个job有没有执行完,定时任务都会启动,如果没有执行完,定时任务会开一个新的线程来执行job,确保能在设定的时间间隔内执行job;定时器默认最多有十个线程,当十个线程都用完时,定时任务会阻塞,直到有新的线程可用,才会开启定时任务去执行job;
2.利用cronTrigger的Java实现
package com.ouku.entities.report; import java.util.Date; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.scheduling.quartz.QuartzJobBean; import com.ouku.report.ReportTask; public class ReportTimerTask extends QuartzJobBean { private int timeout; public void setTimeout(int timeout) { this.timeout = timeout; } @Override protected void executeInternal(JobExecutionContext arg0) throws JobExecutionException { try { Date date = new Date(); String dd = " " + date.getMinutes() + ":" + date.getSeconds() + " "; System.out.println("AAA" + dd); //to do } catch (Exception e) { e.printStackTrace(); } } }
3.利用simpleTrigger的Java实现
package com.ouku.entities.report; import java.util.Date; import org.quartz.JobExecutionException; import com.ouku.report.ReportTask; public class ReportTimerTaskTwo { public void doIt() throws JobExecutionException { try { Date date = new Date(); String dd = " " + date.getMinutes() + ":" + date.getSeconds() + " "; System.out.println("sss1" + dd); //to do .. } catch (Exception e) { e.printStackTrace(); } } }
在使用并发的情况下,当Job执行时间超过间隔时间时,调度框架为了能让任务按照我们预定的时间间隔执行,会马上启用新的线程执行任务。
再次强调,spring4 quartz最多可以为我们开启十个线程,当我们需要执行的任务有锁的情况下,那么在十个线程用完之后,定时器没有线程可以开启,这时候就会出现定时任务的时间间隔超过我们设定的时间间隔;
我们怎么解决这个情况呢?
一、优化我们执行的job,使其在设定的时间间隔内执行完;
二、设置时间间隔更长一点;
我们再来看看串行执行和并发执行的区别:
下面是串行执行时,每个线程的执行情况:
我们可以看到,每个定时任务是在job完成之后才会开启新的线程来执行下一个job;
下面看看并发执行,每个线程执行的情况:
我们可以看到,定时任务是按照我们设置的时间间隔执行的,不会在意job是否执行完,如果没有执行完,定时任务会开启一个新的线程来执行job;