史上最全最详细的Spring定时任务的讲解以及实例

一、最原始的定时任务

1.采用线程方式

public static void runTask(){
        final long timeInterval = 1000;
        Runnable runnable = new Runnable() {
            public void run() {
                while (true){
                    System.out.println("hello");
                    try {
                        Thread.sleep(timeInterval);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
    }

二、采用jdk中的Timer类

package com.flx.timer;

import com.flx.timer.task.SimpleTask;
import com.flx.timer.task.SimpleTaskLiving;
import com.flx.util.BaseFunction;
import com.flx.util.date.CalendarUtils;

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * Created by Fenglixiong on 2017/9/25.
 */
public class TimerHome {

    private static Timer timer = new Timer();

    public static void main(String[] args) {

        TaskOne(10);
        TaskTwo(CalendarUtils.addSeconds(new Date(),10));
        TaskThree(CalendarUtils.addSeconds(new Date(),10));
        TaskFour();
        TaskFive(CalendarUtils.addSeconds(new Date(),10));
        TaskSix();
        TimerTaskPool();
        TimerTask01();
        TimerTask02();
    }

    /**
     * 指定延迟时间执行定时任务
     * @param time
     */
    public static void TaskOne(int time){
        BaseFunction.console("准备执行任务TaskOne...");
        Timer timer = new Timer();
        timer.schedule(new SimpleTask(),time*1000);
    }

    /**
     * 在指定时间执行任务
     * @param date
     */
    public static void TaskTwo(Date date){
        BaseFunction.console("准备执行任务TaskTwo...");
        CalendarUtils.sayTime(new Date());
        Timer timer = new Timer();
        timer.schedule(new SimpleTask(),date);
    }

    /**
     * 在指定时间执行然后以指定时间间隔执行任务
     * @param date
     */
    public static void TaskThree(Date date){
        BaseFunction.console("准备执行任务TaskThree...");
        CalendarUtils.sayTime(new Date());
        Timer timer = new Timer();
        timer.schedule(new SimpleTaskLiving("TaskThree"),date,3000);
    }

    /**
     * 在指定时间延迟之后执行然后以指定时间间隔执行任务
     *
     */
    public static void TaskFour(){
        BaseFunction.console("准备执行任务TaskFour...");
        CalendarUtils.sayTime(new Date());
        Timer timer = new Timer();
        timer.schedule(new SimpleTaskLiving("TaskFour"),5000,3000);
    }

    /**
     * 在指定时间延迟之后执行然后以指定时间间隔执行任务
     *
     */
    public static void TaskFive(Date firstTime){
        BaseFunction.console("准备执行任务TaskFive...");
        CalendarUtils.sayTime(new Date());
        Timer timer = new Timer("TaskFive");
        timer.scheduleAtFixedRate(new SimpleTaskLiving(),firstTime,3000);
    }

    /**
     * 在指定时间延迟之后执行然后以指定时间间隔执行任务
     *
     */
    public static void TaskSix(){
        BaseFunction.console("准备执行任务TaskSix...");
        CalendarUtils.sayTime(new Date());
        Timer timer = new Timer();
        timer.scheduleAtFixedRate(new SimpleTaskLiving("TaskSix"),20000,3000);
    }

    /**
     * TimerTask01();
     * TimerTask02();
     * 同时执行这两个task
     * 1.如果是两个Timer的话相当于两个线程互相不会影响
     * 2.如果是一个Timer的话计划执行两个定时任务的话
     *      其中一个延迟或者阻塞会影响另一个任务的执行
     */
    public static void TimerTask01(){
        CalendarUtils.sayTime(new Date());
        timer.schedule(new TimerTask() {
            public void run() {
                System.out.println("start:TimerTask01");
                try {
                    BaseFunction.console("运行:--->TimerTask01");
                    Thread.sleep(15000);    //线程休眠3000
                    BaseFunction.console("休眠后--->TimerTask01");
                    CalendarUtils.sayTime(new Date());
//                    throw new RuntimeException();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 1000);
    }

    public static void TimerTask02(){
        CalendarUtils.sayTime(new Date());
        timer.schedule(new TimerTask() {
            public void run() {
                System.out.println("运行:TimerTask02");
                CalendarUtils.sayTime(new Date());
//                throw new RuntimeException("...");
            }
        }, 5000);
    }

    /**
     * ScheduledExecutorService是从Java SE5的java.util.concurrent里,
     * 做为并发工具类被引进的,这是最理想的定时任务实现方式。
     */
    public static void TimerTaskPool(){

        Runnable runnable = new Runnable() {
            public void run() {
                // task to run goes here
                System.out.println("Hello !!");
            }
        };

        ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
        executorService.scheduleAtFixedRate(runnable,10,1, TimeUnit.SECONDS);

    }

}

/**
 schedule(TimerTask task, Date time):安排在指定的时间执行指定的任务。
 schedule(TimerTask task, Date firstTime, long period) :安排指定的任务在指定的时间开始进行重复的固定延迟执行。
 schedule(TimerTask task, long delay) :安排在指定延迟后执行指定的任务。
 schedule(TimerTask task, long delay, long period) :安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。
 同时也重载了scheduleAtFixedRate方法,scheduleAtFixedRate方法与schedule相同,只不过他们的侧重点不同,区别后面分析。
 scheduleAtFixedRate(TimerTask task, Date firstTime, long period):安排指定的任务在指定的时间开始进行重复的固定速率执行。
 scheduleAtFixedRate(TimerTask task, long delay, long period):安排指定的任务在指定的延迟后开始进行重复的固定速率执行。
 */

/**
 * TimerTask

 TimerTask类是一个抽象类,由Timer 安排为一次执行或重复执行的任务。它有一个抽象方法run()方法,
 该方法用于执行相应计时器任务要执行的操作。因此每一个具体的任务类都必须继承TimerTask,然后重写run()方法。
 另外它还有两个非抽象的方法:
 boolean cancel():取消此计时器任务。
 long scheduledExecutionTime():返回此任务最近实际执行的安排执行时间。
 */

三、Spring自身集成的Task任务

(1)配置文件方式

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

    <!--此任务写到注解执行了-->
    <!--<task:scheduled-tasks>-->
        <!--<task:scheduled ref="simpleTaskJob" method="jobOne" cron="0/2 * * * * ?"/>-->
    <!--</task:scheduled-tasks>-->
    
    <!--新闻抓取-->
    <task:scheduled-tasks>
        <task:scheduled ref="simpleTaskService" method="sayHello" cron="0/2 * * * * ?" fixed-delay="1000"/>
    </task:scheduled-tasks>

    <!--债券抓取-->
    <task:scheduled-tasks>
        <task:scheduled ref="simpleTaskService" method="sayGood" cron="0/5 * * * * ?" />
    </task:scheduled-tasks>

    <!--上证E互动-->
    <task:scheduled-tasks>
        <task:scheduled ref="simpleTaskService" method="sayLove" cron="0/10 * * * * ?" />
    </task:scheduled-tasks>

    <!--公告-->
    <task:scheduled-tasks>
        <task:scheduled ref="simpleTaskService" method="sayNo" cron="0/15 * * * * ?" />
    </task:scheduled-tasks>

    <!--港交所股票持有数抓取-->
    <task:scheduled-tasks>
        <task:scheduled ref="simpleTaskService" method="sayYes" cron="0/20 * * * * ?" />
    </task:scheduled-tasks>

</beans>

<!--

字段   允许值   允许的特殊字符
秒    0-59    , - * /
分    0-59    , - * /
小时    0-23    , - * /
日期    1-31    , - * ? / L W C
月份    1-12 或者 JAN-DEC    , - * /
星期    1-7 或者 SUN-SAT    , - * ? / L C #
年(可选)    留空, 1970-2099    , - * /
- 区间
* 通配符
? 你不想设置那个字段
下面只例出几个式子

CRON表达式    含义
"0 0 12 * * ?"    每天中午十二点触发
"0 15 10 ? * *"    每天早上10:15触发
"0 15 10 * * ?"    每天早上10:15触发
"0 15 10 * * ? *"    每天早上10:15触发
"0 15 10 * * ? 2005"    2005年的每天早上10:15触发
"0 * 14 * * ?"    每天从下午2点开始到2点59分每分钟一次触发
"0 0/5 14 * * ?"    每天从下午2点开始到2:55分结束每5分钟一次触发
"0 0/5 14,18 * * ?"    每天的下午2点至2:55和6点至6点55分两个时间段内每5分钟一次触发
"0 0-5 14 * * ?"    每天14:00至14:05每分钟一次触发
"0 10,44 14 ? 3 WED"    三月的每周三的14:10和14:44触发
"0 15 10 ? * MON-FRI"    每个周一、周二、周三、周四、周五的10:15触发

-->


(2)注解方式

首先要开启注解驱动

<task:annotation-driven scheduler="poolScheduler" mode="proxy"/>
<task:scheduler id="poolScheduler" pool-size="10"/>

任务类

@Service
public class SimpleTaskJob {

    private int count = 0;

    /**
     * ApplicationContext.xml中配置了注解驱动之后完美执行任务
     * 下面解除注释就可以执行
     */
    @Scheduled(cron = "0/2 * * * * ?")
    public void jobOne() {
        System.out.println("jobOne任务中....."+(++count));
    }

}


四、Spring结合Quartz实现精确定时任务

(1)最标准的写法

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">

    <!--1.增加线程池,用于任务注册-->
    <bean id="poolTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
        <property name="corePoolSize" value="10"/>
        <property name="maxPoolSize" value="20"/>
        <property name="queueCapacity" value="500"/>
    </bean>

    <!--2.定义业务逻辑处理类-->
    <bean id="simpleTask" class="com.flx.quartz.schedule.SimpleTask"/>

    <!--3.增加调度业务逻辑-->
    <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="simpleTask"/>
        <property name="targetMethod" value="doTask"/>
    </bean>

    <!--4.增加调度触发器-->
    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="jobDetail"/>
        <property name="startDelay" value="3000"/>
        <property name="repeatInterval" value="2000"/>
    </bean>
    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="jobDetail"/>
        <property name="cronExpression" value="0/3 * * * * ?"/>
    </bean>

    <!--5.增加调度-->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="simpleTrigger"/>
                <ref bean="cronTrigger"/>
            </list>
        </property>
        <property name="taskExecutor" ref="poolTaskExecutor"/>
    </bean>
</beans>

(2)复杂的Quartz调用

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
	http://www.springframework.org/schema/context
	http://www.springframework.org/schema/context/spring-context-4.0.xsd
	http://www.springframework.org/schema/mvc
	http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

    <!--定时器数据,可选-->
    <bean id="message" class="com.flx.app.entity.Message">
        <property name="status" value="100"/>
        <property name="message" value="very good"/>
    </bean>

    <bean id="methodTask" class="com.flx.quartz.schedule.MethodTask"/>

<!--Job任务类start-->
    <!-- For times when you just need to invoke a method on a specific object -->
    <bean id="simpleTask" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
        <property name="targetObject" ref="methodTask"/>
        <property name="targetMethod" value="sayHello"/>
    </bean>

    <bean id="firstComplexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="com.flx.quartz.schedule.FirstComplexJobDetail"/>
        <property name="jobDataMap">
            <map>
                <entry key="message" value-ref="message"/>
            </map>
        </property>
        <!--即使trigger不关联也不会被删除-->
        <property name="durability" value="true"/>
    </bean>

    <bean id="secondComplexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="com.flx.quartz.schedule.SecondComplexJobDetail"/>
        <property name="durability" value="true"/>
    </bean>


    <!--如果你需要更高级的设置,需要给作业传递数据,想更加灵活的话就使用这种方式。-->
    <bean id="simpleJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
        <property name="jobClass" value="com.flx.quartz.schedule.SimpleQuartzJob"/>
        <property name="jobDataMap">
            <map>
                <entry key="message" value-ref="message"/>
            </map>
        </property>
        <!--如果一个任务不是durable,那么当没有Trigger关联它的时候,它就会被自动删除。-->
        <property name="durability" value="true"/>
        <property name="description" value="简单的作业实现"/>
    </bean>
<!--Job任务类end-->

<!--A简单触发器-->
    <!--配置 Quartz 调度时要使用到的触发器-->
    <bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
        <property name="jobDetail" ref="simpleTask"/>
        <property name="startDelay" value="3000"/>
        <property name="repeatInterval" value="2000"/>
        <property name="repeatCount" value="4"/>
        <!--<property name="startTime" value=""/>-->
    </bean>
<!--B计划触发器-->
    <!--这种类型更加灵活,允许你针对特定实例选择计划方案以及将来要执行的频率。-->
    <bean id="firstCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="firstComplexJobDetail"/>
        <property name="cronExpression" value="0/5 * * ? * *"/>
    </bean>
    <bean id="secondCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="jobDetail" ref="secondComplexJobDetail"/>
        <property name="cronExpression" value="0/10 * * ? * *"/>
    </bean>


<!--将任务和计划全部整合在以前-->
    <bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
            <list>
                <ref bean="simpleTrigger"/>
                <ref bean="firstCronTrigger"/>
                <ref bean="secondCronTrigger"/>
            </list>
        </property>
    </bean>

</beans>

最后开启测试类

public static void main(String[] args) {
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("timer/spring-quartz-task.xml");
        AbstractApplicationContext context = new ClassPathXmlApplicationContext("timer/simple-quartz-task.xml");
    }





猜你喜欢

转载自blog.csdn.net/fenglixiong123/article/details/78133060
今日推荐