@Schedule也可以这么细

使用Spring的定时任务,一些不注意的细节(坑点)给我造成了很大的困扰,有时间来记录一下。

 

1. 基本使用步骤

  • 使用@EnableScheduling 启动定时任务;
  • 编写定时任务类和方法,使用 @Component注入定时类,使用@Scheduled 注解定时方法;

举个栗子:

@Component
public class MySchedule {

    @Scheduled(cron = "0 0/1 * * * ?")
    public void heart() {
        System.out.println("定时执行heart()");
    }
}

 

2. 执行模式有哪些,如何使用?

执行模式有:cron、fixedDelay、fixedRate

  • cron:按照cron表达式代表的执行计划来执行;

cron表达式举栗:(需要什么,网上有生成器)

0 0/1 * * * ? 每分钟执行一次

0 0 1 * * ? 每天1点执行一次

  • fixedDelay 固定延迟执行,在上一次执行完成后,延迟?时间再执行下一次
  • fixedRate 固定速率,以一定的速率执行,这个需要认真理解一下,网上的博客很多都认为这个和fixedDelay 一样,坑啊

 

有一篇博客生动的说明了这三种模式的不同:

fixedDelay

该方式最简单,在上一个任务执行完成之后,间隔3秒(因为@Scheduled(fixedDelay = 3 * 1000))后,执行下一个任务

cron

@Scheduled(cron = "0/5 * * * * ? ")

因此你可以理解为5s就是一个周期.这就相当于在宿舍洗澡,因为只有一个洗澡位置(单线程),所以每次只能进去一个人,然后舍长在门口,每5s看一下有没有空位,有空位的话叫下一个进去洗澡.

第5秒的时候,舍长看了一下,发现第一个同学还没有出来.

第二个周期,也就是第10秒的时候再看一下.发现已经有空位了,那么就叫第二个同学进去洗.

第三个周期,也就是15秒的时候,又瞄了一眼,发现有空位了,叫第三个同学进去洗.

第四个周期,也就是20秒的时候,发现没有空位.

第五个周期的时候,也就是25秒的时候.发现有空位了,接着叫下一个进去洗.剩下的不再分析.

fixedRate

@Scheduled(fixedRate = 5 * 1000)

你可以理解为舍长预算每个同学洗澡的时间是5秒,但是第一个同学进去洗澡,用了8秒.

第二个同学本来应该在第5秒的时候就进去的,结果第一个同学出来的时候,已经是第8秒了,那么舍长就赶紧催第二个同学进去,把时间进度追回来.

第二个同学知道时间紧,洗了3秒就出来.此时舍长发现,第三个同学,原本应该是在第10秒进去的,结果现在已经到了第11秒(8+3),那么就赶紧催第三个同学进去.

第三个同学沉醉其中,难以自拔的洗了6秒.出来的时候已经是第17秒(8+3+6).舍长掐指一算,发现第四个同学原本应该是第15秒的时候就进去了.结果现在都17秒了,时间不等人,催促第四个同学进去赶紧洗.

第四个同学只洗了2秒就出来了,出来的时候,舍长看了一下时间,是第19秒."有原则"的舍长发现,第5个同学原本预算是20秒的时候进去的,结果现在才19秒,不行,那让他在外面玩1秒的手机,等20秒的时候再进去.

 

作者:春苟哈皮

链接:https://www.jianshu.com/p/5eea95b118a1

不同的模式适用于不同的业务场景,斟酌使用。

 

3. 还有哪些坑点

默认所有的@Scheduled方法由单线程调度,没有同时执行的任务!

所以一旦其中一个任务阻塞,其他任务就不会执行了。。。

避免任务中出现死循环无法跳出,http持续等待(记得设置超时时间,坑惨我了)

 

4. 如果有的任务需要大量的时间来运行,但又不想让它拖延其他的任务:

  • 设置异步执行:

使用@EnableAsync开启异步

在方法上使用@Async设置方法异步执行

@Async标记的方法在调用时则立即返回,方法内容则提交异步执行,需要注意的是如果任务用时太长,且任务计划太频繁会出现任务方法多次同时执行的情况

  • 设置定时任务线程池大小
@Configuration
@EnableScheduling
public class ScheduleConfig {
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        return taskScheduler;
    }
}

据说springboot 2.x 可以直接在配置文件中配置线程池大小

spring.task.scheduling.pool.size=10

具体从哪个版本开始的也没查证,我的是2.1.1,可以

 

 

看到网上有人问怎么灵活配置定时任务是否开启:

  • 如果要进行每个任务的单元控制,可以在配置文件中配置开关标记,在每个任务方法中利用标记来增加判断:
@Scheduled(cron = "0 0/1 * * * ?")
public void heart() {
    if (你的标记) {
        // TODO 你的任务
    }
}
  • 如果要控制整个定时任务是否开启,可在配置类中使用
@Configuration
@EnableScheduling
@ConditionalOnProperty(prefix = "schedule", name = "enabled", havingValue = "true")
public class ScheduleConfig {
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        return taskScheduler;
    }
}

在配置文件中增加:

schedule:

    enabled: true

@ConditionalOnProperty注解来控制@Configuration是否生效

 

为事所累,甚是矛盾,待悟。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值