1.实现定时任务的几种方式:

  • crontab 命令 + sql语句

  • python脚本 + sql语句

  • spring + JDK Timer

  • spring + Quartz


2.Quartz概述:

Quartz是java领域中最著名的定时任务调度工具,具有以下特性:

  • 强大的调度功能

  • 灵活的应用方式

  • 负载均衡

  • 高可用特性

  • 易于spring集成

3.基础概念:

Quartz 任务调度的核心元素是 scheduler, trigger 和 job,其中 trigger 和 job 是任务调度的元数据, scheduler 是实际执行调度的控制器。

4.Quartz API:

Scheduler – 与scheduler交互的主要API;

Job – 通过scheduler执行任务,任务类需要实现的接口;

JobDetail – 定义Job的实例;

Trigger – 触发Job的执行;

JobBuilder – 定义和创建JobDetail实例的接口;

TriggerBuilder – 定义和创建Trigger实例的接口;

在 Quartz 中,调度器 scheduler 由 SchedulerFactory创建的,可以增加、删除和列举Job和Trigger,以及执行其它与调度相关的操作。job是用于表示被调度的任务,其接口只有一个execute()方法,在触发时会被scheduler调用。trigger 是用于定义调度时间的元素,常用的类型有:SimpleTrigger,特定的时间点执行,重复执行N次,每次执行间隔T个时间单位;CronTrigger在基于日历的调度,如:每个月第一周的周一晚上。

job与trigger关系:个 job 可以被多个 trigger 关联,但是一个 trigger 只能关联一个 job。

了解了Job和Trigger基础概念之后,看一下任务的唯一定义,也就是key:

将Job和Trigger注册到Scheduler时,可以为它们设置key,配置其身份属性。Job和Trigger的key(JobKey和TriggerKey)可以用于将Job和Trigger放到不同的分组(group)里,然后基于分组进行操作。同一个分组下的Job或Trigger的名称必须唯一,即一个Job或Trigger的key由名称(name)和分组(group)组成。

wKioL1eK8DDAzHmzAAB3EKPhlMM962.png-wh_50

Scheduler 调度线程主要有两个: 执行常规调度的线程,和执行 misfired trigger(失效触发) 的线程。常规调度线程轮询存储的所有 trigger,如果有需要触发的 trigger,即到达了下一次触发的时间,则从任务执行线程池获取一个空闲线程,执行与该 trigger 关联的任务。Misfire 线程是扫描所有的 trigger,查看是否有 misfired trigger,如果有的话根据 misfire 的策略分别处理。

以上为之前一大神分享给我的,接下来是自己的测试代码:wKioL1eK--mSJ0GyAAAOnciHmkw556.png-wh_50

这些包是我springmvc配完后加上去的,少一个都会报错。

然后是springMVC配置文件中的配置,如下:

<!-- 配置方法调用器  -->
	<bean name="exampleJob" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
         <property name="targetObject">  
              <bean class= "cn.hygj.glg.controller.IndexController" /> <!-- 执行类 -->
         </property >
         <property name= "targetMethod">
              <value>execute</value>  <!-- 执行方法名-->
         </property>
    </bean >
	<!-- 	配置定时触发器    -->
    <bean id="CronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean" >
         <property name= "jobDetail" ref ="exampleJob"/>
         <property name= "cronExpression" value ="0/10 * * * * ?"/>   <!-- 定时表达式 -->
         <property name="startDelay" value="5000"/><!--延迟触发 value的值表示启动后延迟多久开始执行调度 -->
     </bean >
     <!-- 	配置简单触发器    -->
    <bean id="SimpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean" >
         <property name= "jobDetail" ref ="exampleJob"/>
         <property name= "repeatInterval" value ="25000"/>   <!-- 任务执行间隔, 注:该时间不能不配置,或配置为0-->
         <property name="repeatCount" value="5"/><!--任务执行次数 -->
     	
     </bean >
	<!-- 该方法调用一次,仅为输出tomcat启动完成的时间  -->
    <bean id="TestTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean" >
         <property name= "jobDetail" ref ="exampleJob"/>
         <property name= "repeatInterval" value ="1"/>   <!-- 任务执行间隔 -->
         <property name="repeatCount" value="1"/><!--任务执行次数 -->
     
     </bean >
     
    <!--配置任务调度工厂 将触发器配置进去 可配置多个  -->
     <bean id="schedulerFactory" lazy-init="false" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" >
         <property name= "triggers">
             <list>
                 <ref bean= "CronTrigger"/>
                 <ref bean= "SimpleTrigger"/>
                 <ref bean= "TestTrigger"/>
             </list>
         </property>
     </bean >

执行的方法:

public void execute() {
		System.out.println("任务开始执行,执行时间:"+new Date());
		}

接下来是测试结果:

wKiom1eK_pKg67XjAAA91axbiQU184.png-wh_50

很明显,间隔8秒的执行了5次就停了,定时的那个5秒后再开始,且每个10秒执行。

接下来是定时任务的表达式:

cron表达式用于CronTrigger示例,可以定制灵活的计划任务,它的组成包括6、7个域(子表达式),每个域代表一种时间类型,按照顺序如下:

wKiom1eLAmPjFG9JAAArnuuNOV0999.png-wh_50

特殊字符含义:

'*' 表示域中"每个"。比如在"Minutes"域中的*表示每分钟。

'?' 用在day-of-month及day-of-week域中,表示"没有指定值"。这对于需要指定一个或者两个域的值而不需要对其他域进行设置来说相当有用。例如,我想在一个月的某一天(例如,第十),而不在乎具体是哪一天,我会把"10"放在day-of-month 域,然后"?"在day-of-week里。

'-' 指定范围,例如,"10-12"在Hours域,表示10点到12点。

',' 指定附加值,例如,"MON,WED,FRI"在day-of-week域中,表示"星期一,星期三和星期五"。

'/' 没有具体的值,用来用于指定值的增量,例如, 如果在Seconds域中,'0/10',它表示“从0开始,每隔10秒”。

'L' 只用在day-of-month及day-of-week中,这个字符是"last"的简写,但是在两个域中的意义不同。例如,在day-of-month域中的"L"表示本月的最后一天,即,一月的31日,非闰年的二月的28日。如果它用在day-of-week中,则表示"7"或者"SAT"。但是,这个字符跟在别的值后面,则表示"当月的最后的周XXX"。例如:"6L" 或者 "FRIL"都表示本月的最后一个周五。同时,也可以用来指定第某个月的最后一天的倒数第几天,如“L-3”表示某月最后一天的倒数第三天。注意:当使用'L'选项时,最重要的是不要指定列表或者值范围,否则会导致混乱。

'W' 用于day-of-week域中指定给定日(星期一星期五)最近的一天。例如:"15W",则表示"距离月中15号最近的工作日是周几"。

'#' 表示本月中的第几个周几。例如:day-of-week域中的"6#3" 或者 "FRI#3"表示"本月中第三个周五"。

一些简单的例子:

0/10 * * * * ?   每10秒

0 0 1 ? * MON  每周一凌晨1点

0 0 9 * * ?       每天早上9点

0 0 10 L-2 * ?  每个月最后一天倒数第二天上午10点