SpringBoot 实战: Spring Boot 3种定时任务方式

一、什么是定时任务

在项目开发过程中,经常会使用到定时任务。顾名思义,定时任务一般指定时执行的方法。例如,每天凌晨0点同步 A 系统的数据到 B 系统;每2小时统计用户的积分情况;每周一给支付宝用户推送上周收入支出数据报表等等。
一般情况下,很多业务会定时在凌晨进行处理。因为这能避开用户使用高峰期,空闲时服务器资源充足,而且对用户影响小。
通过 Spring Boot 框架,我们可以使用3种方式来实现定时任务。
  • 第1种是基于注解的方式,比较常用,但是这种在程序运行过程种不能动态更改定时任务的时间。
  • 第2种是可以动态更改定时任务的时间。
  • 第3种是可以动态更改定时任务的时间,还可以动态启动,停止定时任务。

一、代码示例

@Scheduled 定时任务

使用 Spring Boot 内置的注解方式,即在需要定时执行的方法上添加@Scheduled注解即可。定时执行的方法不能有参数,并且一般没有返回值,即使有返回值也没什么用。注意定时任务所在的类要作为 Spring Bean,在类上添加@Component注解即可。

package com.chenpi.springschedule.task;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * @author 
 * @version 1.0
 * @description 定时任务类
 * @date 2021/3/2
 */

@Component
public class ScheduledTask {

  private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTask.class);

  // 每5秒执行一次
  @Scheduled(cron = "0/5 * * * * ? ")
  public void test() {
    LOGGER.info(">>>>> ScheduledTask doing ...");
  }
}

然后在启动类上添加@EnableScheduling注解开启定时任务。默认情况下,系统会自动启动一个线程,调度执行项目中定义的所有定时任务。

package com.chenpi.springschedule;

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

@SpringBootApplication
@EnableScheduling
public class Application {

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

}

启动项目,即可在控制台中每5秒看到定时任务被执行

2022-04-11 20:46:55.011  INFO 11800 --- [ scheduling-1] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask doing ...
2022-04-11 20:47:00.014  INFO 11800 --- [ scheduling-1] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask doing ...
2022-04-11 20:47:05.003  INFO 11800 --- [ scheduling-1] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask doing ...
2022-04-11 20:47:10.003  INFO 11800 --- [ scheduling-1] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask doing ...
2022-04-11 20:47:15.001  INFO 11800 --- [ scheduling-1] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask doing ...

多线程定时任务

使用@Scheduled注解形式的定时任务,默认是单个线程来执行项目中的所有定时任务的。即使如果同一时刻有两个定时任务需要执行,那么也只能其中一个定时任务完成之后再执行下一个定时任务。如果项目只有一个定时任务还好。如果定时任务增多时,如果一个任务被阻塞,则会导致其他任务无法正常执行。

可以配置任务调度线程池,来解决以上问题。首先配置一个线程池,如下所示:

package com.chenpi.springschedule.config;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

/**
* @author 
* @version 1.0
* @description 线程池配置
* @date 2021/3/2
*/

@Configuration
public class ExecutorConfig {
    
    public static final int CORE_POOL_SIZE = 2;
    public static final int MAX_POOL_SIZE = 8;
    public static final int QUEUE_CAPACITY = 100;
    
    @Bean("myExecutor")
    public Executor asyncServiceExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 核心线程数大小
        executor.setCorePoolSize(CORE_POOL_SIZE);
        // 最大线程数大小
        executor.setMaxPoolSize(MAX_POOL_SIZE);
        // 阻塞队列容量
        executor.setQueueCapacity(QUEUE_CAPACITY);
        // 线程名前缀
        executor.setThreadNamePrefix("myTask-");
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

然后通过@Async注解添加到定时任务方法上

package com.chenpi.springschedule.task;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

/**
 * @author 
 * @version 1.0
 * @description 定时任务类
 * @date 2021/3/2
 */

@Component
public class ScheduledTask {

  private static final Logger LOGGER = LoggerFactory.getLogger(ScheduledTask.class);

  @Scheduled(fixedRate = 1000)
  @Async("myExecutor")
  public void test() {
    try {
      // 休眠5秒
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    LOGGER.info(">>>>> ScheduledTask doing ...");
  }

  @Scheduled(fixedRate = 1000)
  @Async("myExecutor")
  public void test01() {
    try {
      // 休眠5秒
      Thread.sleep(5000);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
    LOGGER.info(">>>>> ScheduledTask01 doing ...");
  }
}

终输出结果如下,发现多个定时任务是由线程池中的不同线程来执行了。

2022-04-11 22:00:44.483  INFO 15668 --- [ myTask-1] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask doing ...
2022-04-11 22:00:44.483  INFO 15668 --- [ myTask-2] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask01 doing ...
2022-04-11 22:00:49.498  INFO 15668 --- [ myTask-2] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask01 doing ...
2022-04-11 22:00:49.498  INFO 15668 --- [ myTask-1] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask doing ...
2022-04-11 22:00:54.507  INFO 15668 --- [ myTask-2] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask doing ...
2022-04-11 22:00:54.507  INFO 15668 --- [ myTask-1] c.c.springschedule.task.ScheduledTask    : >>>>> ScheduledTask01 doing ...







原文始发于微信公众号(Java技术前沿):SpringBoot 实战: Spring Boot 3种定时任务方式

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/299710.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!