美文网首页javaspring boot
spring boot中的定时任务

spring boot中的定时任务

作者: 大浪滔滔 | 来源:发表于2018-01-16 17:28 被阅读91次

    在spring boot中,简单的任务调度工作可以由@Scheduled注解来完成。

    如果需要深入了解spring的调度,最好的方式是先看下官方文档

    一、Spring的任务抽象接口

    TaskExecutor接口

    TaskExecutor是任务执行接口,类似于java.util.concurrent.Executor,该接口只有一个方法execute(Runnable task),用于执行任务。

    Spring提供了一组TaskExecutor的实现,基本上能满足所有的需求。它们的使用方式跟普通的Spring Bean一样。

    TaskScheduler接口

    TaskScheduler接口是定时器的抽象,它的源代码如下。可以看到,该接口包含了一组方法用于指定任务执行的时间。

    public interface TaskScheduler {
    
        ScheduledFuture schedule(Runnable task, Trigger trigger);
    
        ScheduledFuture schedule(Runnable task, Date startTime);
    
        ScheduledFuture scheduleAtFixedRate(Runnable task, Date startTime, long period);
    
        ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
    
        ScheduledFuture scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
    
        ScheduledFuture scheduleWithFixedDelay(Runnable task, long delay);
    
    }
    

    二、实操

    2.1、定时任务

    具体步骤如下:

    1. 在应用入口增加@EnableScheduling

    @SpringBootApplication
    @EnableScheduling
    public class Application {
    
        public static void main(String [] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

    如果应用依赖了spring-boot-starter-actuator,即使没有声明@EnableScheduling,调度任务也会被执行。因为spring-boot-starter-actuator本身也声明了@EnableScheduling,见MetricExportAutoConfiguration

    2. 增加任务调度服务

    @Component // 或@Service
    public class TaskService {
    
        @Scheduled(fixedRate = 1000) // 每隔1秒执行一次
        public void timerRate() {
            // 在此处执行调度任务。
        }
    }
    

    @Scheduled 注解主要使用在执行调度任务的方法上。

    注意!Springboot 默认的执行方式是串行执行,无论有多少task(@Scheduled),都是一个线程串行执行。同一个task,如果前一个还没跑完后面一个就不会触发,不同的task也不能同时运行,这是因为scheduler的默认线程数为1的缘故。

    在高可用的情况下,如果微服务有多个实例,scheduler会在多个实例上同时运行。

    2.2、并行任务

    在上面的基础上,增加一个实现SchedulingConfigurer接口的类:

    @Configuration
    public class ScheduleConfig implements SchedulingConfigurer {
    
        private final int POOL_SIZE = 10;
     
        @Override
        public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
            ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
    
            threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
            // 线程前缀
            threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
            threadPoolTaskScheduler.initialize();
    
            scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
        }
    
      /*  @Override
        public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
            taskRegistrar.setScheduler(taskExecutor());
        }
     
        @Bean(destroyMethod="shutdown")
        public Executor taskExecutor() {
            return Executors.newScheduledThreadPool(POOL_SIZE); // 线程池初始化10个线程
        }*/
    }
    

    如果在调度方法里打印当前线程名称:

    Current Thread : my-scheduled-task-pool-1
    Current Thread : my-scheduled-task-pool-2
    ...
    

    配置多个线程会导致同一个task前一个还没跑完后面又被触发的问题。

    附录1、@Scheduled中的参数

    (1) cron:cron表达式,指定任务在特定时间执行;
    (2) fixedDelay:表示上一次任务执行完成后多久再次执行,参数类型为long,单位ms;
    (3) fixedDelayString:与fixedDelay含义一样,只是参数类型变为String;
    (4) fixedRate:表示按一定的频率执行任务,参数类型为long,单位ms;
    (5) fixedRateString: 与fixedRate的含义一样,只是将参数类型变为String;
    (6) initialDelay:表示延迟多久再第一次执行任务,参数类型为long,单位ms;
    (7) initialDelayString:与initialDelay的含义一样,只是将参数类型变为String;
    (8) zone:时区,默认为当前时区,一般没有用到。

    例子:

    • @Scheduled(fixedRate=1000): 上一次开始执行后1秒再次执行;
    • @Scheduled(fixedDelay=1000):上一次执行完成后1秒再次执行;
    • @Scheduled(initialDelay=1000, fixedDelay=3000):第一次延迟1秒执行,然后在上一次执行完成后3秒再次执行;
    • @Scheduled(cron="* * * * * ?"):按cron表达式执行。

    附录2、CRON表达式

    Cron格式

    Quartz的官方文档

    一个 Cron表达式是由6或7个字段(年字段是可选字段)的字符串组成,字段与字段之间用空格来隔开。
    Cron表达式使用格式:

    Seconds Minutes Hours DayofMonth Month DayofWeek [Year]
    [年]

    各个域的定义:

    必输 含义 允许值 允许特殊符号
    Seconds 0-59 , - * /
    Minutes 0-59 , - * /
    Hours 0-23 , - * /
    Day of Month 1-31 , - * ? / L W
    Month 1-12或者 JAN-DEC , - * /
    Day of Week 1-7 或者 SUN-SAT , - * ? / L #
    [Year] [年] 空,1970-2099 , - * /

    特殊符号代表的含义:

    符号 含义
    * 匹配该域的任意值;如*用在所在的域,表示每分钟都会触发事件。
    ? 无特定值。只能用在DayofMonthDayofWeek两个域。 由于DayofMonthDayofWeek这两个元素是互斥的,必须要对其中一个设置?。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用,而不能使用*,如果使用*表示不管星期几都会触发。
    - 匹配一个特定的范围值;如所在的域的值是10-12,表示10、11、12点的时候会触发事件.
    , 匹配多个指定的值;如所在的域的值是2,4,6,表示在周一、周三、周五就会触发事件。(1表示周日,2表示周一,3表示周二,以此类推,7表示周六)。
    / 指定数值的增量。左边是开始触发时间,右边是每隔固定时间触发一次事件,如秒所在的域的值是5/15,表示从第5秒开始,每15秒触发,即在第5秒、20秒、35秒、50秒的时候都触发一次事件,等价于“5,20,35,50”。
    L last,最后的意思,如果是用在这个域,表示月的最后一天,如果是用在周所在的域,如6L,表示某个月最后一个周五。
    W weekday,工作日的意思。如所在的域的值是15W,表示本月15日最近的工作日,如果15日是周六,触发器将触发上14日周五。如果15日是周日,触发器将触发16日周一。如果15日不是周六或周日,而是周一至周五的某一个,那么它就在15日当天触发事件。
    # 用来指定每个月的第几个星期几,如6#3表示某个月的第三个星期五。

    CRON表达式官方例子

    表达式 含义
    "0 0 12 * * ?" 每天12:00触发事件
    "0 15 10 ? * *" 每天10:15触发事件
    "0 15 10 * * ?" 每天10:15触发事件
    "0 15 10 * * ? *" 每天10:15触发事件
    "0 15 10 * * ? 2005" 2005年的每天10:15触发事件
    "0 * 14 * * ?" 每天14点开始触发,每分钟触发一次,14:59分结束
    "0 0/5 14 * * ?" 每天14点开始触发到14:59分结束的每5分钟触发一次事件
    "0 0/5 14,18 * * ?" 每天14点开始到14:59期间和18点到18:59期间的每5分钟触发一次事件
    "0 0-5 14 * * ?" 每天14点到14:05期间的每1分钟触发一次事件
    "0 10,44 14 ? 3 WED" 每年3月的星期三的14:10和14: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触发一次事件

    相关文章

      网友评论

        本文标题:spring boot中的定时任务

        本文链接:https://www.haomeiwen.com/subject/bcgsoxtx.html