原创

SpringBoot的定时器@Scheduled注解使用方法

温馨提示:
本文最后更新于 2022年09月27日,已超过 550 天没有更新。若文章内的图片失效(无法正常加载),请留言反馈或直接联系我

pom 包里面只需要引入 Spring Boot Starter 包即可

<dependencies>
	<dependency>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter</artifactId>
	</dependency>
</dependencies>

1.在Application启动类上添加@EnableScheduling注解启动定时(这个相当于定时器总开关,不加不会生效)

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
 
@SpringBootApplication
@EnableScheduling
public class TaskApplication {
    public static void main(String[] args) {
        SpringApplication.run(TaskApplication.class, args);
    }
 
}

2.TestController类使用@Component注解,交给Spring容器管理。具体方法使用@Scheduled(cron="0/5 * * * * ?")标注,表示该方法从0秒开始,每隔5秒执行一次 方法。

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
@Component
public class TestController{
    /**
     *  每五秒执行一次
     */
    @Scheduled(cron="0/5 * * * * ?")
    public void executeFileDownLoadTask() {
        System.out.println("定时任务启动");
    }
}

注:单线程操作到这一步直接启动项目就可以了。

3.异步执行任务使用@EnableAsync和@Async

事实上在Spring的定时任务包中提供了@EnableAsync@Async注解用于多线程异步执行任务。
首先在启动类上添加@EnableAsync注解,并在TestController类上标注@Async注解,表示该类中所有标注了@Scheduled的方法都使用异步处理方式。

Spring中@EnableScheduling和@Scheduled标注的定时任务默认是单线程执行的,多线程操作,任务1执行任务需要花费较长时间,所已阻塞了任务2的执行。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
 
@SpringBootApplication
@EnableScheduling@EnableAsync
public class TaskApplication {
    public static void main(String[] args) {
        SpringApplication.run(TaskApplication.class, args);
    }
 
}
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
@Component@Async
public class TestController{
    /**
     *  每五秒执行一次
     */
    @Scheduled(cron="0/5 * * * * ?")
    public void executeFileDownLoadTask() {
        System.out.println("定时任务启动");    Thread t = Thread.currentThread();
        System.out.println("taskSchule2 "+ DateTimeUtils.dateToTimeStr(new Date()) +" ThreadID:"+  t.getId() +" "+t.getName());
    }
}

此时,任务1和任务2均运行正常,并且任务1和任务2都是不同线程在执行,不会出现任务之间相互阻塞的情况。

4.创建一个任务配置类ScheduleConfig 

去掉Application和TestController中的@EnableAsync@Async@EnableScheduling注解。创建一个任务配置类ScheduleConfig实现SchedulingConfigurer接口的configureTasks方法,使用参数taskRegistrar为任务调度创建线程池

@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);
    }
}

 多线程 现在任务1和任务2均运行正常,并且任务1不会出现坑2中的交叉现象,任务1第二次调度会等到第一次调度执行完毕后地下一个调度时间点才会执行。

总结

SpringBoot中可以使用@EnableScheduling和@Scheduled注解实现定时任务调度,但是注意默认所有任务都被单个线程调度的,有可能任务之间发生阻塞现象,可以使用@EnableAsync和@Async注解实现异步多线程任务调度,但需要注意任务执行时间如果大于任务调度周期时间,可能出现同一个任务交叉执行的情况。当然也可以使用4方法避免上述问题

Cron表达式

Cron表达式由7个部分组成,各部分用空格隔开,Cron表达式的7个部分从左到右代表的含义如下:

cron表达式格式:
{秒数} {分钟} {小时} {日期} {月份} {星期} {年份(可为空)}
 
Cron表达式范例:
*/5 * * * * ? :每隔5秒执行一次
0 */1 * * * ? :每隔1分钟执行一次
0 0 23 * * ? :每天23点执行一次
0 0 1 * * ? :每天凌晨1点执行一次:
0 0 1 1 * ? :每月1号凌晨1点执行一次
0 0 23 L * ? : 每月最后一天23点执行一次
0 0 1 ? * L :每周星期天凌晨1点实行一次
0 26,29,33 * * * ? : 在26分、29分、33分执行一次
0 0 0,13,18,21 * * ? : 每天的0点、13点、18点、21点都执行一次

符号说明

,:表示列出枚举值值。例如在分使用5,20,则意味着在5和20分每分钟触发一次。
-:表示范围。例如在分使用5-20,表示从5分到20分钟每分钟触发一次。
* :表示匹配该域的任意值。假如在分域使用*,即表示每分钟都会触发事件。
/ :表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次。
? :只能用在周和日。它也匹配域的任意值,但实际不会。因为周和日会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?,其中最后一位只能用?,而不能使用,如果使用表示不管星期几都会触发,实际上并不是这样。
L : 表示最后,只能出现在日和周,如果在日使用5L,意味着在最后的一个星期四触发。
W:表示有效工作日(周一到周五),只能出现在周域,系统将在离指定日期的最近的有效工作日触发事件。例如:在日使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日触发;如果5日在星期一到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份。
#:用于确定每个月第几个星期几,只能出现在周。例如在4#2,表示某月的第二个星期三。

正文到此结束
该篇文章的评论功能已被站长关闭
本文目录