引言
在项目开发过程中,定时任务扮演着至关重要的角色,它们按照预定的时间自动执行特定任务,如数据同步、统计汇总、报告推送等。例如,每日凌晨0点自动将A系统的数据同步至B系统,每两小时统计并更新用户的积分情况,以及每周一自动向支付宝用户发送上周的收入支出数据报表。选择凌晨执行这些任务,主要是为了避免业务高峰期,确保服务器资源充足,并减少对用户正常使用的影响。
在Spring Boot框架中,实现定时任务通常可以采用以下几种方式,每种方式都有其特点和适用场景:
-
基于注解的方式:这是最常见且简便的方法,通过@Scheduled注解即可轻松实现定时任务。然而,这种方式的局限性在于,一旦程序启动,定时任务的执行时间便固定下来,难以在运行时动态调整。
-
动态调整定时任务时间:为了克服第一种方式的不足,可以采用能够动态调整定时任务时间的方法。这通常涉及到更复杂的配置和编程,如使用TaskScheduler接口结合ScheduledFuture来管理定时任务,从而允许在程序运行时根据需求调整任务的执行时间。
-
动态启动与停止定时任务:更进一步地,为了实现更高的灵活性和控制力,可以设计一种机制,允许不仅动态调整定时任务的执行时间,还能动态地启动或停止这些任务。这通常涉及到对定时任务进行更细致的管理,如使用ScheduledExecutorService或结合Spring的TaskScheduler和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