Spring Boot学习笔记----Schedule

有时需要服务器定时,或者每隔多长时间去做某件事情,Spring boot使用注解@Scheduled实现该功能。

方法概述

Scheduled共有5个方法
(1)initialDelay:在第一次执行前,需要Delay的时间。
(2)fixedRate:间隔固定的时间执行,不论是上次执行是否完毕。
(3)fixedDelay:在前一次执行后,Delay一段时间,然后再次执行。
(4)cron:固定的时间段执行,共有7个参数。
(5)zone:与cron配合使用,指定所使用的时区。若不设置,则使用当前时区。

需要注意的是,方法中所用到的时间单位为毫秒。

使用@Scheduled的前提条件

(1)需要将Application添加@EnableScheduling

package com.breakloop.taskdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class TaskdemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(TaskdemoApplication.class, args);
    }
}

(2)使用@Scheduled方法的类,必须进行了注册声明。例如@Component

package com.breakloop.taskdemo;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class myTaskList {

    private static Logger logger= LoggerFactory.getLogger(myTaskList.class);

    private static final long initDelay=3000l;

    private static final long delayFixed =1000l;

    @Scheduled(fixedDelay = delayFixed)
    public void task1(){
        logger.info("task1");
        try {
            Thread.sleep(1000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

下面分别来看几种方法。

fixedDelay

代码中的Task1便使用了fixedDelay。在输出“Task1”后,等待1秒。

执行结果如下

2017-12-06 23:21:53.526  INFO 8636 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1
2017-12-06 23:21:53.526  INFO 8636 --- [on(3)-127.0.0.1] c.breakloop.taskdemo.ServletInitializer  : Started ServletInitializer in 4.004 seconds (JVM running for 8.255)
[2017-12-06 11:21:53,574] Artifact taskdemo:war: Artifact is deployed successfully
[2017-12-06 11:21:53,574] Artifact taskdemo:war: Deploy took 6,189 milliseconds
2017-12-06 23:21:54.219  INFO 8636 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-12-06 23:21:54.250  INFO 8636 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 31 ms
2017-12-06 23:21:55.532  INFO 8636 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1
06-Dec-2017 23:21:57.083 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [C:\D\apache-tomcat-9.0.1\webapps\manager]
06-Dec-2017 23:21:57.161[ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [C:\D\apache-tomcat-9.0.1\webapps\manager] has finished in [78] ms
2017-12-06 23:21:57.562  INFO 8636 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1
2017-12-06 23:21:59.569  INFO 8636 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1

从执行结果,可以得出以下结论
(a)从时间上看,第N+1次执行确实在第N次后,Delay了1秒钟。
(b)第一次执行,在Started ServletInitializer前就开始了

因此,为了保证必要的初始化完成,使用initialDelay方法是非常有必要的。

initialDelay

我们对Task1的声明进行修改。添加initialDelay方法。

    @Scheduled(initialDelay = initDelay,fixedDelay = delayFixed)
    public void task1(){
        logger.info("task1");
        try {
            Thread.sleep(1000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

执行结果如下

2017-12-06 23:31:01.691  INFO 8380 --- [on(5)-127.0.0.1] c.breakloop.taskdemo.ServletInitializer  : Started ServletInitializer in 4.033 seconds (JVM running for 8.209)
[2017-12-06 11:31:01,738] Artifact taskdemo:war: Artifact is deployed successfully
[2017-12-06 11:31:01,738] Artifact taskdemo:war: Deploy took 6,163 milliseconds
2017-12-06 23:31:02.378  INFO 8380 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization started
2017-12-06 23:31:02.409  INFO 8380 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : FrameworkServlet 'dispatcherServlet': initialization completed in 31 ms
2017-12-06 23:31:04.687  INFO 8380 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1
06-Dec-2017 23:31:05.276 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [C:\D\apache-tomcat-9.0.1\webapps\manager]
06-Dec-2017 23:31:05.348 [ContainerBackgroundProcessor[StandardEngine[Catalina]]] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [C:\D\apache-tomcat-9.0.1\webapps\manager] has finished in [72] ms
2017-12-06 23:31:06.689  INFO 8380 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1
2017-12-06 23:31:08.701  INFO 8380 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1

从结果可见,task1在Artifact is deployed successfully之后才执行,达到预期目的。

fixedRate

在myTaskList中添加Task2,使用fixedRate方法。
同时,为避免启动过程影响执行效果,将initDelay更新未7秒。

    @Scheduled(initialDelay = initDelay,fixedRate =delayFixed )
    public void task2(){
        logger.info("task2");
        try {
            Thread.sleep(1000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

执行结果如下

2017-12-06 23:45:54.443  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:45:55.444  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1
2017-12-06 23:45:56.444  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:45:57.453  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:45:58.462  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:45:59.476  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1
2017-12-06 23:46:00.477  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:46:01.479  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:46:02.479  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:46:03.493  INFO 7416 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2

由结果可以得出以下结论
(a)虽然上次Task2还未执行完,但每秒都会启动一个新的Task2。
(b)由INFO 7416 — [pool-1-thread-1]可知,所有的执行,都是串行的,在同一线程中完成!

那么问题来了,如何让任务并行呢?

并发设置

实现任务的并行,需要以下几个步骤
(1)创建类(本例中为SchedulConfig),添加注释@Configuration
(2)为类添加注释@EnableScheduling
(2)该类实现SchedulingConfigurer接口,并实现方法
public void configureTasks(ScheduledTaskRegistrar taskRegistrar),为Task指定Executor。

全部代码如下

package com.breakloop.taskdemo;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

@Configuration
@EnableScheduling
public class SchedulConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(taskExecutor());
    }

    public Executor taskExecutor(){
        return Executors.newScheduledThreadPool(10);
    }
}

从代码中可见,Task的Executor是一个容量为10的线程池。

我们再来执行一下Task1,2
执行结果如下

2017-12-06 23:53:58.318  INFO 8080 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:53:58.318  INFO 8080 --- [pool-1-thread-2] com.breakloop.taskdemo.myTaskList        : task1
2017-12-06 23:53:59.324  INFO 8080 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:54:00.325  INFO 8080 --- [pool-1-thread-3] com.breakloop.taskdemo.myTaskList        : task1
2017-12-06 23:54:00.325  INFO 8080 --- [pool-1-thread-2] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:54:01.332  INFO 8080 --- [pool-1-thread-4] com.breakloop.taskdemo.myTaskList        : task2
2017-12-06 23:54:02.346  INFO 8080 --- [pool-1-thread-1] com.breakloop.taskdemo.myTaskList        : task1
2017-12-06 23:54:02.346  INFO 8080 --- [pool-1-thread-5] com.breakloop.taskdemo.myTaskList        : task2

可见,Task1,2在同一个线程池中的不同线程中运行。

cron

通过以上三种方法,可以有间隔的执行任务。但并不能满足所有需求。例如,服务器只需要在每天的8点到20点间,每10分钟执行一次任务,或者在固定的时间点执行任务。此时,cron便派上用场。

我们先写一下Task3,之后记录cron参数的含义。同时,将delayFixed 延长至20秒,便于观察结果。

    @Scheduled(cron = "0/10 45-47 0 * * ?")
    public void task3(){
        logger.info("task3");
        try {
            Thread.sleep(1000l);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

执行结果如下图

这里写图片描述

cron的传参为String类型,由7部分组成,各部分用空格分隔。从前往后含义依次如下

(1)秒,有效值0~59
(2)分钟,有效值0~59
(3)小时,有效值0~23
(4)天数,有效值0~30
(5)月,有效值0~11
(6)星期几,有效值1~7。1是星期天,然后依次是星期一,二…六
(7)年,有效期1970~2099

需要注意的是,参数由(1)至(7)或(1)至(6)组成。
此外,(4)与(7)冲突,因此必须有一个为转义符“?”,表示不确定。
当然,还有其他的转义符。

转义符

“?”,表示不确定。
“*”,表示不做要求。
“,”,表示多个数据。例如“30 29,30,31 0 * * ?”表示每天凌晨0点29分30秒,30分30秒,31分30秒,各执行一次。
“-”,表示区间,0-5,表示[0,5],例如“30 29,30,31 0 * * ?”等同于“30 29-31 0 * * ?”。
“/”,表示间隔,0/5,表示每隔5秒(或者分钟,小时,天,月)。
“L”,表示最后的XXX。例如”0 15 10 ? * 6L 2002-2005” 2002年至2005年的每月的最后一个星期五上午10:15执行。
“#”,表示第几个XXX。例如”0 15 10 ? * 6#3” 每月的第三个星期五上午10:15执行。

因此,Task3中”0/10 45-47 0 * * ?”的含义是, 每天凌晨0点45分至47分之间,每10秒执行。

zone

zone很少被用到,网上的资料也不多。翻看源码,可知其输入为String,跟java.util.TimeZone.getTimeZone(String)的传参应该一致。

这里写图片描述
此处,未做实验,之后补上。

至此,Schedule总结完成。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值