Spring Boot学习笔记----Schedule
2017-12-07 01:07
295 查看
有时需要服务器定时,或者每隔多长时间去做某件事情,Spring boot使用注解@Scheduled实现该功能。
(1)initialDelay:在第一次执行前,需要Delay的时间。
(2)fixedRate:间隔固定的时间执行,不论是上次执行是否完毕。
(3)fixedDelay:在前一次执行后,Delay一段时间,然后再次执行。
(4)cron:固定的时间段执行,共有7个参数。
(5)zone:与cron配合使用,指定所使用的时区。若不设置,则使用当前时区。
需要注意的是,方法中所用到的时间单位为毫秒。
(2)使用@Scheduled方法的类,必须进行了注册声明。例如@Component
下面分别来看几种方法。
执行结果如下
从执行结果,可以得出以下结论
(a)从时间上看,第N+1次执行确实在第N次后,Delay了1秒钟。
(b)第一次执行,在Started ServletInitializer前就开始了。
因此,为了保证必要的初始化完成,使用initialDelay方法是非常有必要的。
执行结果如下
从结果可见,task1在Artifact is deployed successfully之后才执行,达到预期目的。
同时,为避免启动过程影响执行效果,将initDelay更新未7秒。
执行结果如下
由结果可以得出以下结论
(a)虽然上次Task2还未执行完,但每秒都会启动一个新的Task2。
(b)由INFO 7416 — [pool-1-thread-1]可知,所有的执行,都是串行的,在同一线程中完成!
那么问题来了,如何让任务并行呢?
(1)创建类(本例中为SchedulConfig),添加注释@Configuration
(2)为类添加注释@EnableScheduling
(2)该类实现SchedulingConfigurer接口,并实现方法
public void configureTasks(ScheduledTaskRegistrar taskRegistrar),为Task指定Executor。
全部代码如下
从代码中可见,Task的Executor是一个容量为10的线程池。
我们再来执行一下Task1,2
执行结果如下
可见,Task1,2在同一个线程池中的不同线程中运行。
我们先写一下Task3,之后记录cron参数的含义。同时,将delayFixed 延长至20秒,便于观察结果。
执行结果如下图
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秒执行。
此处,未做实验,之后补上。
至此,Schedule总结完成。
方法概述
Scheduled共有5个方法(1)initialDelay:在第一次执行前,需要Delay的时间。
(2)fixedRate:间隔固定的时间执行,不论是上次执行是否完毕。
(3)fixedDelay:在前一次执行后,Delay一段时间,然后再次执行。
(4)cron:固定的时间段执行,共有7个参数。
(5)zone:与cron配合使用,指定所使用的时区。若不设置,则使用当前时区。
需要注意的是,方法中所用到的时间单位为毫秒。
使用@Scheduled的前提条件
(1)需要将Application添加@EnableSchedulingpackage 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总结完成。
相关文章推荐
- Spring Boot 学习笔记1---初体验之3分钟启动你的Web应用
- cocos2dx学习笔记之回调函数:定时器schedule,普通回调callfunc,菜单回调menu_selector
- Spring Boot 学习笔记(二)——使用
- 我的Cocos2d-x学习笔记(十)定时调度器(scheduleUpdate、scheduleOnce、schedule)
- cocos2d-x学习笔记(13)--schedule
- spring boot 学习笔记(4) 动态创建定时任务
- Spring Boot学习笔记:(三)Web开发(Thymeleaf)
- spring boot 学习笔记(05)——热部署 之spring-boot-devtools
- spring boot 学习笔记(005)提交json对象
- spring boot 学习笔记
- spring boot 学习笔记(002) Hello world
- spring boot学习笔记【0】开篇
- Spring boot学习笔记 001——初识Spring boot
- Spring Boot学习笔记03--深入了解SpringBoot的启动过程
- Spring Boot 学习笔记(二)
- spring boot 学习笔记
- Cocos2d-x学习笔记(14)(更新函数scheduleUpdate、进度计时器CCProgressTo、滚动视图CCScrollView)
- Spring Boot学习笔记(一)
- 【Cocos2d-x3.0学习笔记 11】Schedule的使用01
- spring boot的学习笔记