javaspring boot

spring boot中的定时任务

2018-01-16  本文已影响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:时区,默认为当前时区,一般没有用到。

例子:

附录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触发一次事件
上一篇下一篇

猜你喜欢

热点阅读