springboot使用@Scheduled做定时任务,以及连接池问题

本人觉得@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的博客-CSDN博客

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中向我咨询

关注我的公众号,获取更多关于后端、大数据的知识

  • 28
    点赞
  • 113
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值