spring boot: 计划任务@ EnableScheduling和@Scheduled

@Scheduled中的参数说明

@Scheduled(fixedRate=2000):上一次开始执行时间点后2秒再次执行; 

@Scheduled(fixedDelay=2000):上一次执行完毕时间点后2秒再次执行;

@Scheduled(initialDelay=1000, fixedDelay=2000):第一次延迟1秒执行,然后在上一次执行完毕时间点后2秒再次执行; 

@Scheduled(cron="* * * * * ?"):按cron规则执行。  

常用Cron表达式(  秒/分/时/日/月/周/年  )

0 0 10,14,16 * * ? 每天上午10点,下午2点,4点

0 0/30 9-17 * * ?   朝九晚五工作时间内每半小时

0 0 12 ? * WED 表示每个星期三中午12点

"0 0 12 * * ?" 每天中午12点触发

"0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发

"0 0/5 14 * * ?" 在每天下午2点到下午2:55期间的每5分钟触发

"0 0/5 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发

"0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发

"0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发

"0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发

"0 15 10 15 * ?" 每月15日上午10:15触发

"0 15 10 L * ?" 每月最后一日的上午10:15触发

"0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发

"0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发

"0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发

"0 15 10 ? * *" 每天上午10:15触发

"0 15 10 * * ?" 每天上午10:15触发

"0 15 10 * * ? *" 每天上午10:15触发

"0 15 10 * * ? 2005" 2005年的每天上午10:15触发

  

实例:

service

复制代码

 1 package ch2.scheduler;
2
3 //时间处理,时间格式化
4 import java.util.Date;
5 import java.text.SimpleDateFormat;
6
7
8 //spring计划任务声明(针对方法声明)
9 import org.springframework.scheduling.annotation.Scheduled;
10 //spring组件声明
11 import org.springframework.stereotype.Service;
12
13
14 //组件什么
15 @Service
16 public class SchedulerService {
17
18 //创建日期模板
19 private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH::MM::ss");
20
21
22 //每5秒钟执行一次
23 @Scheduled(fixedRate = 5000)
24 public void reportCurrentTime()
25 {
26 System.out.println("每五秒执行一次: " + dateFormat.format( new Date() ));
27 }
28
29 //按照cron属性指定执行时间:秒分时
30 @Scheduled(cron = "0 34 18 ? * *")
31 public void fixTimeExecution()
32 {
33 System.out.println("在指定的时间内执行: " + dateFormat.format( new Date()) );
34 }
35
36
37 }
复制代码 config 复制代码 1 package ch2.scheduler;
2
3 //自动引入包
4 import org.springframework.context.annotation.ComponentScan;
5 //配置类声明
6 import org.springframework.context.annotation.Configuration;
7
8 //计划任务类声明
9 import org.springframework.scheduling.annotation.EnableScheduling;
10
11
12 //配置类声明
13 @Configuration
14 //自动引入包
15 @ComponentScan("ch2.scheduler")
16 //通过@EnableScheduling开启对计划任务的支持
17 @EnableScheduling
18 public class TaskSchedulerConfig {
19
20
21
22 }
复制代码 main 复制代码 1 package ch2.scheduler;
2 //引入容器
3 import org.springframework.context.annotation.AnnotationConfigApplicationContext;
4
5 public class Main {
6
7
8 public static void main(String[] args)
9 {
10 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TaskSchedulerConfig.class);
11 //SchedulerService schedulerService = context.getBean(SchedulerService.class);
12
13
14 }
15 }

要注意什么坑

不绕弯子了,直接说这个坑是啥:

SpringBoot使用@scheduled定时执行任务的时候是在一个单线程中,如果有多个任务,其中一个任务执行时间过长,则有可能会导致其他后续任务被阻塞直到该任务执行完成。也就是会造成一些任务无法定时执行的错觉

可以通过如下代码进行测试:

    @Scheduled(cron = "0/1 * * * * ? ")
public void deleteFile() throws InterruptedException {
log.info("111delete success, time:" + new Date().toString());
Thread.sleep(1000 * 5);//模拟长时间执行,比如IO操作,http请求
} @Scheduled(cron = "0/1 * * * * ? ")
public void syncFile() {
log.info("222sync success, time:" + new Date().toString());
} /**输出如下:
[pool-1-thread-1] : 111delete success, time:Mon Nov 26 20:42:13 CST 2018
[pool-1-thread-1] : 222sync success, time:Mon Nov 26 20:42:18 CST 2018
[pool-1-thread-1] : 111delete success, time:Mon Nov 26 20:42:19 CST 2018
[pool-1-thread-1] : 222sync success, time:Mon Nov 26 20:42:24 CST 2018
[pool-1-thread-1] : 222sync success, time:Mon Nov 26 20:42:25 CST 2018
[pool-1-thread-1] : 111delete success, time:Mon Nov 26 20:42:25 CST 2018
上面的日志中可以明显的看到syncFile被阻塞了,直达deleteFile执行完它才执行了
而且从日志信息中也可以看出@Scheduled是使用了一个线程池中的一个单线程来执行所有任务的。
**/ /**如果把Thread.sleep(1000*5)注释了,输出如下:
[pool-1-thread-1]: 111delete success, time:Mon Nov 26 20:48:04 CST 2018
[pool-1-thread-1]: 222sync success, time:Mon Nov 26 20:48:04 CST 2018
[pool-1-thread-1]: 222sync success, time:Mon Nov 26 20:48:05 CST 2018
[pool-1-thread-1]: 111delete success, time:Mon Nov 26 20:48:05 CST 2018
[pool-1-thread-1]: 111delete success, time:Mon Nov 26 20:48:06 CST 2018
[pool-1-thread-1]: 222sync success, time:Mon Nov 26 20:48:06 CST 2018
这下正常了
**/

解决办法

1.将@Scheduled注释的方法内部改成异步执行
如下:

//当然了,构建一个合理的线程池也是一个关键,否则提交的任务也会在自己构建的线程池中阻塞
ExecutorService service = Executors.newFixedThreadPool(5); @Scheduled(cron = "0/1 * * * * ? ")
public void deleteFile() {
service.execute(() -> {
log.info("111delete success, time:" + new Date().toString());
try {
Thread.sleep(1000 * 5);//改成异步执行后,就算你再耗时也不会印象到后续任务的定时调度了
} catch (InterruptedException e) {
e.printStackTrace();
}
});
} @Scheduled(cron = "0/1 * * * * ? ")
public void syncFile() {
service.execute(()->{
log.info("222sync success, time:" + new Date().toString());
});
}

2.把Scheduled配置成成多线程执行

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
//当然了,这里设置的线程池是corePoolSize也是很关键了,自己根据业务需求设定
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(5)); /**为什么这么说呢?
假设你有4个任务需要每隔1秒执行,而其中三个都是比较耗时的操作可能需要10多秒,而你上面的语句是这样写的:
taskRegistrar.setScheduler(Executors.newScheduledThreadPool(3));
那么仍然可能导致最后一个任务被阻塞不能定时执行
**/
}
}
  

最新文章

  1. (一)NOR FALSH 讲解
  2. python内置数据类型-字典和列表的排序 python BIT sort——dict and list
  3. 转:从编译链接过程解析static函数的用法
  4. 3、android notification 详细用法
  5. C#WebClient常见用法
  6. MySql中的变量定义
  7. main(int argc , char *argv[])
  8. A/B测试评测
  9. openwrt uci
  10. 解决ecshop3.6 H5版本公告页面为空的修改办法
  11. 数据结构 - 表插入排序 具体解释 及 代码(C++)
  12. Tea for Mac(mac笔记软件)中文版
  13. H5 五子棋源码
  14. 2883 -- 【TJOI2018】游园会
  15. 什么是node
  16. centos7 安装oracle11g
  17. MySQL数据库事务各隔离级别加锁情况--read committed && MVCC(转载)
  18. mysql小试题
  19. 【题解】Power Strings
  20. cmder的使用和编码问题解决

热门文章

  1. (原)ubuntu中C++调用libotrch
  2. linux查看磁盘类型(是否SSD盘)
  3. 在 Visual Studio 中安装 FxCop 分析器
  4. mysql基础知识之数据类型与约束
  5. USB总线驱动程序
  6. 201871010105-曹玉中《面向对象程序设计(Java)》第二周学习总结
  7. 201871020225-牟星源 《面向对象程序设计(java)》课程学习进度条
  8. 09-numpy-笔记-repeat
  9. js面向对象杂谈
  10. git bisect