我们在平常项目开发中,经常会用到周期性定时任务,这个时候使用定时任务就能很方便的实现。在SpringBoot中用得最多的就是Schedule。

一、SpringBoot集成Schedule

1、依赖配置

由于Schedule就包含在spring-boot-starter中,所以无需引入其他依赖。

2、启用定时任务

在启动类或者配置类上增加@EnableScheduling注解。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling; @EnableScheduling
@SpringBootApplication
public class DemoApplication { public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
} }

3、添加定时任务

Schdule支持cron表达式、固定间隔时间、固定频率三种调度方式。

1)cron表达式定时任务

与Linux下定时任务用到的Cron表达式一样。

字段 允许值 允许的特殊字符
秒(Seconds) 0~59的整数 , - * /    四个字符
分(Minutes) 0~59的整数 , - * /    四个字符
小时(Hours 0~23的整数 , - * /    四个字符
日期(DayofMonth 1~31的整数(但是你需要考虑该月的天数) ,- * ? / L W C     八个字符
月份(Month 1~12的整数或者 JAN-DEC , - * /    四个字符
星期(DayofWeek 1~7的整数或者 SUN-SAT (1=SUN) , - * ? / L C #     八个字符
年(可选,留空)(Year 1970~2099 , - * /    四个字符
@Component
@EnableScheduling
public class MyCronTask { private static final Logger logger = LoggerFactory.getLogger(MyCronTask.class); @Scheduled(cron = "0/1 * * * * *")
void cronSchedule(){
logger.info("cron schedule execute");
} }

PS:Cron表达式方式配置的定时任务如果其执行时间超过调度频率时,调度器会在下个执行周期执行。如第一次执行从第0秒开始,执行时长3秒,则下次执行为第4秒。

2)固定间隔定时任务

下一次的任务执行时间是从上一次定时任务结束时间开始计算。

@Scheduled(fixedDelay = 2)
void fixedDelaySchedule() throws Exception{
Thread.sleep(2000);
logger.info("fixed delay schedule execute");
}

输出:

2020-04-23 23:11:54.362  INFO 85325 --- [   scheduling-1] com.springboot.study.tasks.MyCronTask    : fixed delay schedule execute
2020-04-23 23:11:58.365 INFO 85325 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : fixed delay schedule execute
2020-04-23 23:12:02.372 INFO 85325 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : fixed delay schedule execute
2020-04-23 23:12:06.381 INFO 85325 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : fixed delay schedule execute

3)固定频率定时任务

按照指定频率执行任务

@Scheduled(fixedRate = 2000)
void fixedRateSchedule() throws Exception{
Thread.sleep(3000);
logger.info("fixed rate schedule execute");
}

输出:

2020-04-23 23:16:14.750  INFO 85328 --- [   scheduling-1] com.springboot.study.tasks.MyCronTask    : fixed rate schedule execute
2020-04-23 23:16:17.754 INFO 85328 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : fixed rate schedule execute
2020-04-23 23:16:20.760 INFO 85328 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : fixed rate schedule execute
2020-04-23 23:16:23.760 INFO 85328 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : fixed rate schedule execute
2020-04-23 23:16:26.764 INFO 85328 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : fixed rate schedule execute

PS:当方法的执行时间超过任务调度频率时,调度器会在当前方法执行完成后立即执行下次任务。

二、配置多个定时任务并发执行

1、并行or串行?

缺省状态下,当我们没有给定时任务配置线程池时,Schedule是串行执行,如下:

@Component
@EnableScheduling
public class MyCronTask { private static final Logger logger = LoggerFactory.getLogger(MyCronTask.class); @Scheduled(fixedDelay = 2000)
void task1Schedule() throws Exception{
Thread.sleep(2000);
logger.info("task1 execute");
} @Scheduled(fixedDelay = 2000)
void task2Schedule() throws Exception{
Thread.sleep(2000);
logger.info("task2 execute");
} @Scheduled(fixedDelay = 2000)
void task3Schedule() throws Exception{
Thread.sleep(2000);
logger.info("task3 execute");
} }

输出:

2020-04-23 23:19:46.970  INFO 85332 --- [   scheduling-1] com.springboot.study.tasks.MyCronTask    : task1 execute
2020-04-23 23:19:48.973 INFO 85332 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : task2 execute
2020-04-23 23:19:50.974 INFO 85332 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : task3 execute
2020-04-23 23:19:52.978 INFO 85332 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : task1 execute
2020-04-23 23:19:54.984 INFO 85332 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : task2 execute
2020-04-23 23:19:56.984 INFO 85332 --- [ scheduling-1] com.springboot.study.tasks.MyCronTask : task3 execute

可以看出来只有一个线程穿行执行所有定时任务。

2、Schedule并行执行配置

定时调度的并行化,有两种配置方式:

1)修改任务调度器默认使用的线程池:添加一个configuration,实现SchedulingConfigurer接口就可以了。

@Configuration
public class ScheduleConfig implements SchedulingConfigurer{ @Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setTaskScheduler(getTaskScheduler());
} @Bean
public TaskScheduler getTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setPoolSize(3);
taskScheduler.setThreadNamePrefix("myworker-");
taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
return taskScheduler;
}
}

再次执行后,输出:

2020-04-23 23:33:14.197  INFO 85461 --- [     myworker-2] com.springboot.study.tasks.MyCronTask    : task2 execute
2020-04-23 23:33:14.197 INFO 85461 --- [ myworker-1] com.springboot.study.tasks.MyCronTask : task1 execute
2020-04-23 23:33:14.197 INFO 85461 --- [ myworker-3] com.springboot.study.tasks.MyCronTask : task3 execute
2020-04-23 23:33:18.203 INFO 85461 --- [ myworker-2] com.springboot.study.tasks.MyCronTask : task2 execute
2020-04-23 23:33:18.203 INFO 85461 --- [ myworker-3] com.springboot.study.tasks.MyCronTask : task1 execute
2020-04-23 23:33:18.204 INFO 85461 --- [ myworker-1] com.springboot.study.tasks.MyCronTask : task3 execute
2020-04-23 23:33:22.208 INFO 85461 --- [ myworker-1] com.springboot.study.tasks.MyCronTask : task3 execute
2020-04-23 23:33:22.208 INFO 85461 --- [ myworker-2] com.springboot.study.tasks.MyCronTask : task2 execute
2020-04-23 23:33:22.208 INFO 85461 --- [ myworker-3] com.springboot.study.tasks.MyCronTask : task1 execute

2)直接将任务交给一步线程池处理:启用@EnableAsync注解,并在每一个定时任务方法上使用@Async注解。

@Component
@EnableScheduling
@EnableAsync
@Async
public class MyCronTask { private static final Logger logger = LoggerFactory.getLogger(MyCronTask.class); @Scheduled(fixedDelay = 2000)
void task1Schedule() throws Exception{
Thread.sleep(2000);
logger.info("task1 execute");
} @Scheduled(fixedDelay = 2000)
void task2Schedule() throws Exception{
Thread.sleep(2000);
logger.info("task2 execute");
} @Scheduled(fixedDelay = 2000)
void task3Schedule() throws Exception{
Thread.sleep(2000);
logger.info("task3 execute");
} }

输出如下:

2020-04-23 23:38:00.614  INFO 85468 --- [         task-1] com.springboot.study.tasks.MyCronTask    : task1 execute
2020-04-23 23:38:00.614 INFO 85468 --- [ task-3] com.springboot.study.tasks.MyCronTask : task3 execute
2020-04-23 23:38:00.614 INFO 85468 --- [ task-2] com.springboot.study.tasks.MyCronTask : task2 execute
2020-04-23 23:38:02.620 INFO 85468 --- [ task-4] com.springboot.study.tasks.MyCronTask : task1 execute
2020-04-23 23:38:02.620 INFO 85468 --- [ task-5] com.springboot.study.tasks.MyCronTask : task2 execute
2020-04-23 23:38:02.620 INFO 85468 --- [ task-6] com.springboot.study.tasks.MyCronTask : task3 execute

有上面输出可以看出来这种方式对于每一次定时任务的执行都会创建新的线程,这样对内存资源是一种浪费,严重情况下还会导致服务挂掉,因此为了更好控制线程的使用,我们可以自定义线程池。

首先配置线程池:

@Configuration
public class MyTaskExecutor { @Bean(name = "myExecutor")
public TaskExecutor getMyExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(3);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(20);
taskExecutor.setThreadNamePrefix("myExecutor-");
taskExecutor.initialize();
return taskExecutor;
}
}

使用我们自己的线程池:

@Component
@EnableScheduling
@EnableAsync
@Async("myExecutor")
public class MyCronTask { private static final Logger logger = LoggerFactory.getLogger(MyCronTask.class); @Scheduled(fixedDelay = 2000)
void task1Schedule() throws Exception{
Thread.sleep(2000);
logger.info("task1 execute");
} @Scheduled(fixedDelay = 2000)
void task2Schedule() throws Exception{
Thread.sleep(2000);
logger.info("task2 execute");
} @Scheduled(fixedDelay = 2000)
void task3Schedule() throws Exception{
Thread.sleep(2000);
logger.info("task3 execute");
} }

输出:

2020-04-23 23:46:47.404  INFO 85488 --- [   myExecutor-1] com.springboot.study.tasks.MyCronTask    : task1 execute
2020-04-23 23:46:47.404 INFO 85488 --- [ myExecutor-3] com.springboot.study.tasks.MyCronTask : task3 execute
2020-04-23 23:46:47.404 INFO 85488 --- [ myExecutor-2] com.springboot.study.tasks.MyCronTask : task2 execute
2020-04-23 23:46:49.404 INFO 85488 --- [ myExecutor-3] com.springboot.study.tasks.MyCronTask : task2 execute
2020-04-23 23:46:49.404 INFO 85488 --- [ myExecutor-2] com.springboot.study.tasks.MyCronTask : task3 execute
2020-04-23 23:46:49.404 INFO 85488 --- [ myExecutor-1] com.springboot.study.tasks.MyCronTask : task1 execute
2020-04-23 23:46:51.405 INFO 85488 --- [ myExecutor-2] com.springboot.study.tasks.MyCronTask : task2 execute
2020-04-23 23:46:51.405 INFO 85488 --- [ myExecutor-3] com.springboot.study.tasks.MyCronTask : task1 execute
2020-04-23 23:46:51.405 INFO 85488 --- [ myExecutor-1] com.springboot.study.tasks.MyCronTask : task3 execute

最新文章

  1. RTP与RTCP协议介绍(转载)
  2. MyEclipse中的SVN操作手册
  3. Java堆外内存的使用
  4. [BTS] RFC IDOC_INBOUND_ASYNCHRONOUS
  5. 【转】 SVM算法入门
  6. Linux 的 Crontab 命令运用(转)
  7. 南阳oj 求N!的二进制表示最低位的1的位置(从右向左数)。
  8. 用 jQuery Masonry 插件创建瀑布流式的页面(转)
  9. Tomcat中JSP引擎工作原理
  10. P - Atlantis - hdu1542(求面积)
  11. 64位内核注冊tty设备
  12. Eclipse中使用Maven新建 Servlet 2.5的 SpringMVC项目
  13. Go 初体验 - 闭包,数组,切片,锁
  14. spark获取时间
  15. angular2 pipe实现搜索结果中的搜索关键字高亮
  16. TCP和UDP基本原理
  17. PyCharm+Miniconda3安装配置教程
  18. vue过渡
  19. BT1120时序,可以用于自测用
  20. DevExpress02、RibbonControl

热门文章

  1. 【NX二次开发】Block UI 字符串
  2. 6.11考试总结(NOIP模拟7)
  3. 【题解】Luogu p2016 战略游戏 (最小点覆盖)
  4. noip2009 总结
  5. 微信sdk上传图片大小1k,损坏的问题以及微信上传图片需要的配置
  6. Devops 改变coding —— 安装个指定版本的 jenkins 发现和想象的不太一样?
  7. 关于Word转Markdown的工具Writage安装及使用
  8. 【知识点】C++的日志框架
  9. Innodb中有哪些锁?
  10. Java:Java实例化(new)过程