本人觉得@Scheduled使用起来太方便了,大大减少了代码量(2月份刚来公司的时候,还单纯以为只用java的Timer来写呢),突然觉得springboot特别便利....。好了,不多说,开始写@Scheduled博客了。
这里就说一些定时任务的简单实用。首先需要在启动类中加上@EnableScheduling注解来开启定时任务。
@SpringBootApplication
@EnableTransactionManagement
@EnableScheduling
public class SmartSchoolApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(SmartSchoolApplication.class, args);
}
}
配置完后就可以使用定时任务了,例子如下:
@Component
public class TestSchedule {
@Scheduled(fixedDelay = 1000 ) //每一秒运行一次
public void printDate(){
System.out.println(new Date().toLocaleString());
}
}
作为程序员,只会这个肯定是不够的,上面的定时任务时每秒钟执行一次逻辑代码,但是我想每天的上午八点执行一次,此定时任务就不合适了,除非我们在逻辑代码中做时间判断,判断当前时间是不是早上的8点。这样做固然可以,但是这样做的话是不是有点low。我们需要改进的话,就需要说说@Scheduled三种定时任务的执行方式。
@Scheduled有三种定时任务的执行方式,包括fixedDelay、fixedRate、corn表达式,下面就分别讲讲这三种执行方式的不同。
fixedDelay:指定两次任务执行的时间间隔(毫秒),此时间间隔指的是,前一次任务结束与下一个任务开始的间隔。如:@Scheduled(fixedDelay = 5*1000 ),表示第一个任务结束后,过5秒后,开始第二个任务。
fixedRate:指定两次任务执行的时间间隔(毫秒),此时间间隔指的是,前一个任务开始与下一个任务开始的间隔。如:@Scheduled(fixedRate= 5*1000 ),表示第一个任务开始后(第一个任务执行时间小于5秒),第一个任务开始后的第6秒,开始第二个任务。如果第一个任务执行时间大于5秒,第一个任务结束后,直接开始第二个任务。
fixedDelay与fixedRate差别可以查看图例区别:一张图让你秒懂Spring @Scheduled定时任务的fixedRate,fixedDelay,cron执行差异_applebomb的博客-优快云博客
cron:使用表达是进行任务的执行,例如:@Scheduled(cron = "0/15 * * * * ? ")每隔15秒执行一次
cron一般是六个或七个字段,分别是:
1. Seconds (秒)
2. Minutes (分)
3. Hours (时)
4. Day (每月的第几天,day-of-month)
5. Month (月)
6. Day (每周的第几天,day-of-week)
7. Year (年 可选字段)
每隔字段的范围以及特殊字符
秒 :范围:0-59
分 :范围:0-59
时 :范围:0-23
天(月) :范围:1-31,但要注意一些特别的月份2月份没有只能1-28,有些月份没有31
月 :用0-11 或用字符串 “JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC” 表示
天(周):用1-7表示(1 = 星期日)或用字符口串“SUN, MON, TUE, WED, THU, FRI and SAT”表示
年:范围:1970-2099
“/”:表示为“每”,如“0/10”表示每隔10分钟执行一次,“0”表示为从“0”分开始, “3/20”表示表示每隔20分钟执行一次
“?”:只用于月与周,表示不指定值
“L”:只用于月与周,5L用在月表示为每月的最后第五天天;1L用在周表示每周的最后一天;
“W”::表示有效工作日(周一到周五),只能出现在day-of-month,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份
“#”:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。
“*” 代表整个时间段。
注意:每个元素可以是一个值(如6),一个连续区间(9-12),一个间隔时间(8-18/4)(/表示每隔4小时),一个列表(1,3,5),通配符。由于"月份中的日期"和"星期中的日期"这两个元素互斥的,必须要对其中一个设置‘?’
表达式实例(网上搜的,感觉例子都一样, = =):
0 0 10,14,16 * * ? 每天上午10点,下午2点,4点
0 0/30 9-17 * * ? 朝九晚五工作时间内每半小时
0 0 12 ? * WED 表示每个星期三中午12点
"0 0 12 * * ?" 每天中午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期间的每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
-------------------------------------------------分隔符---------------------------------------------------
上面的所有基本上满足大部分的定时任务。但是,你有没有想过,如果写多个@Scheduled,你会发现他们是使用的同一个线程,意不意外,惊不惊喜。在我自己的测试中,所有的定时任务确实是使用的一个线程,测试例子如下:
@Component
public class TestSchedule {
Logger logger = LoggerFactory.getLogger(TestSchedule.class);
@Scheduled(fixedDelay = 1000) //定时任务1
public void printXXXXXXX(){
try{
Thread.sleep(5000); //睡眠5秒
logger.info(Thread.currentThread().getName()); //打印当前线程名字
}catch (Exception e){
logger.error(e.getMessage());
}
}
@Scheduled(fixedDelay = 1000) //定时任务2
public void printYYYYYYY(){
try{
Thread.sleep(5000);
logger.info(Thread.currentThread().getName());
}catch (Exception e){
logger.error(e.getMessage());
}
}
}
运行上面的代码,查看日志。
运行后你会发现,他们使用的是同一个线程,而且你会发现printXXXXXXX原本每6秒(定时任务1000+sleep 5000)执行一次的,现在变成了每10秒执行一次,原因就是受到了printYYYYYYY的Thread.sleep(5000)的影响。这对注重时间范围的定时任务影响特别大,此时就需要定时任务的线程池的配置。
@Configuration
@EnableScheduling
public class ScheduleConfig implements SchedulingConfigurer {
@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskExecutor());
}
@Bean(destroyMethod="shutdown")
public Executor taskExecutor() {
return Executors.newScheduledThreadPool(10); //指定线程池大小
}
}
上面的配置只是增加了线程池大小,可以根据项目的定时任务多少进行配置。这样的话,定时任务之间就不会相互影响,添加这个配置后,继续运行测试代码,日志如下:
github上有我更多的笔记:Raray-chuan (兮川) · GitHub,欢迎stars与following,如果有问题可以在issue中向我咨询
关注我的公众号,获取更多关于后端、大数据的知识