所有分类
  • 所有分类
  • 未分类

Spring定时任务–@Scheduled注解–使用/教程/实例

简介

说明

本文用示例介绍SpringBoot的@Scheduled注解的用法。

执行时间的配置

在方法上使用@Scheduled注解来设置任务的执行时间,并且使用三种属性配置方式:

fixedRatefixedDelaycron
描述每隔多少时间就启动任务,不管该任务是否启动完成每次执行任务完成之后间隔多久再次执行该任务。根据cron表达式执行
执行时机项目启动时即开始项目启动时即开始根据cron表达式执行

initialDelay:指定第一次执行的延时时间。

如:@Scheduled(initialDelay = 1000, fixedRate = 3000) :第一次在延迟1秒后执行,之后按fixedRate的规则每 3 秒执行一次。

配置文件中指定cron

假如application.yml中指定:custom.schedule.cron.task1: 0/5 * * * * *

代码中可以这么写:@Scheduled(cron = “${custom.schedule.cron.task1: 0/10 * * * * *}”)

基础实例

前提条件

添加SpringBoot依赖

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

启用定时器(主类添加@EnableScheduling)

@SpringBootApplication
@EnableScheduling
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

示例1

@Component
public class MyTest {
    @Scheduled(cron = "0/5 * * * * *")
    public void scheduled() {
        System.out.println("this is cron");
    }
}

执行结果

this is cron: 1589472135001

示例2

@Component
public class MyTest {
    @Scheduled(cron = "0/5 * * * * *")
    public void scheduled1() {
        System.out.println("this is cron: " + System.currentTimeMillis());
    }

    @Scheduled(fixedRate = 5000)
    public void scheduled2(){
        System.out.println("this is fixedRate: " + System.currentTimeMillis());
    }

    @Scheduled(fixedDelay = 5000)
    public void scheduled3(){
        System.out.println("this is fixedDelay: " + System.currentTimeMillis());
    }
}

执行结果(它们是串行执行的)

this is cron: 1589472145001
this is fixedRate: 1589472146341
this is fixedDelay: 1589472146342
this is cron: 1589472150002
this is fixedRate: 1589472151342
this is fixedDelay: 1589472151343
this is cron: 1589472155001
this is fixedRate: 1589472156342
this is fixedDelay: 1589472156343

单线程问题

问题描述

简介

@Scheduled默认是单线程执行的,如果前一个任务执行时间过长,则后一个任务会被延迟,示例如下:

测试类

package com.example.tmp;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class MyTest {
    @Scheduled(cron = "0/1 * * * * ?")
    public void task1() {
        log.info("task1");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Scheduled(cron = "0/1 * * * * ?")
    public void task2() {
        log.info("task2");
    }
}

启动类

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@EnableScheduling
@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

执行结果:(前一个任务执行完,才会执行下一个任务)

2021-06-09 23:20:26.006  INFO 2488 --- [   scheduling-1] com.example.tmp.MyTest                   : task1
2021-06-09 23:20:31.021  INFO 2488 --- [   scheduling-1] com.example.tmp.MyTest                   : task2
2021-06-09 23:20:32.007  INFO 2488 --- [   scheduling-1] com.example.tmp.MyTest                   : task1
2021-06-09 23:20:37.020  INFO 2488 --- [   scheduling-1] com.example.tmp.MyTest                   : task2
2021-06-09 23:20:38.015  INFO 2488 --- [   scheduling-1] com.example.tmp.MyTest                   : task1
2021-06-09 23:20:43.017  INFO 2488 --- [   scheduling-1] com.example.tmp.MyTest                   : task2
2021-06-09 23:20:44.013  INFO 2488 --- [   scheduling-1] com.example.tmp.MyTest                   : task1
2021-06-09 23:20:49.027  INFO 2488 --- [   scheduling-1] com.example.tmp.MyTest                   : task2
2021-06-09 23:20:50.014  INFO 2488 --- [   scheduling-1] com.example.tmp.MyTest                   : task1

解决方案

正确的方案

方案1:配置定时任务线程池的个数

修改application.yml 

spring.task.scheduling.pool.size = 10

或者:

@Configuration
public class ScheduleConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(50));
    }
}

方案2:自定义定时任务的线程池 

package com.example.tmp;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class ScheduleConfig {
 
    /**
     * 此处方法名为Bean的名字,方法名无需固定
     * 因为是按TaskScheduler接口自动注入
     */
    @Bean
    public TaskScheduler taskScheduler(){
        // Spring提供的定时任务线程池类
        ThreadPoolTaskScheduler taskScheduler=new ThreadPoolTaskScheduler();
        //设定最大可用的线程数目
        taskScheduler.setPoolSize(10);
        return taskScheduler;
    }
}

错误的方案

错误的方案:异步

不要这么用:启动类加@EnableAsync,Scheduled加@Async

原因:@Async的使用场景和这个不同,用在这里并不合适,虽然也会实现多线程,但是会产生任务重复执行的问题,也会使 fixedDelay策略失效。 ​

0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录