SpringBoot实战:SpringBoot三种定时任务实现方式

引言

在项目开发过程中,定时任务扮演着至关重要的角色,它们按照预定的时间自动执行特定任务,如数据同步、统计汇总、报告推送等。例如,每日凌晨0点自动将A系统的数据同步至B系统,每两小时统计并更新用户的积分情况,以及每周一自动向支付宝用户发送上周的收入支出数据报表。选择凌晨执行这些任务,主要是为了避免业务高峰期,确保服务器资源充足,并减少对用户正常使用的影响。

Spring Boot框架中,实现定时任务通常可以采用以下几种方式,每种方式都有其特点和适用场景

  1. 基于注解的方式这是最常见且简便的方法,通过@Scheduled注解即可轻松实现定时任务。然而,这种方式的局限性在于,一旦程序启动,定时任务的执行时间便固定下来,难以在运行时动态调整。

  2. 动态调整定时任务时间:为了克服第一种方式的不足,可以采用能够动态调整定时任务时间的方法。这通常涉及到更复杂的配置和编程,如使用TaskScheduler接口结合ScheduledFuture来管理定时任务,从而允许在程序运行时根据需求调整任务的执行时间。

  3. 动态启动与停止定时任务更进一步地,为了实现更高的灵活性和控制力,可以设计一种机制,允许不仅动态调整定时任务的执行时间,还能动态地启动或停止这些任务。这通常涉及到对定时任务进行更细致的管理,如使用ScheduledExecutorService或结合SpringTaskScheduler和Trigger接口,通过编程方式控制任务的启动、停止以及执行频率的变更



一、代码示例

@Scheduled 定时任务

@Seheduled 注解,可以轻松安排方法的执行。重要的是,由于这些定时执行的方法通常不接收任何参数,并且其返回值即便存在,也主要用于方法内部的逻辑处理,并不直接参与Spring框架的调度机制。

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

}

多线程定时任务

如果项目只有一个定时任务还好 @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 ...");
  }
}

之后再启动类上添加 @EnableAsync 注解来开启异步事件支持。

package com.chenpi.springschedule;

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

@SpringBootApplication
@EnableScheduling
@EnableAsync
public class Application {

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

}

动态更改时间的定时任务:

实现SchedulingConfigurer接口,并且重写configureTasks方法

package com.chenpi.springschedule.task;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;

/**
 * @author 
 * @version 1.0
 * @description 可动态更改时间的定时任务
 * @date 2021/3/2
 */

@Component
public class ChangeTimeScheduledTask implements SchedulingConfigurer {

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

  // cron表达式,我们动态更改此属性的值即可更改定时任务的执行时间
  private String expression = "0/5 * * * * *";

  @Override
  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    // 定时任务要执行的方法
    Runnable task = () -> LOGGER.info(">>> configureTasks ...");
    // 调度实现的时间控制
    Trigger trigger = triggerContext -> {
      CronTrigger cronTrigger = new CronTrigger(expression);
      return cronTrigger.nextExecutionTime(triggerContext);
    };
    taskRegistrar.addTriggerTask(task, trigger);
  }

  public String getExpression() {
    return expression;
  }

  public void setExpression(String expression) {
    this.expression = expression;
  }
}

之后创建一个接口,来动态改变定时任务的时间:

package com.chenpi.springschedule.controller;

import com.chenpi.springschedule.task.ChangeTimeScheduledTask;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author 
 * @version 1.0
 * @description
 * @date 2021/3/2
 */

@RestController
@RequestMapping("demo")
public class DemoController {

  private final ChangeTimeScheduledTask changeTimeScheduledTask;

  public DemoController(final ChangeTimeScheduledTask changeTimeScheduledTask) {
    this.changeTimeScheduledTask = changeTimeScheduledTask;
  }

  @GetMapping
  public String testChangeTimeScheduledTask() {
    changeTimeScheduledTask.setExpression("0/10 * * * * *");
    return "ok";
  }
}


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

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

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

(0)
小半的头像小半

相关推荐

发表回复

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