【Java定时任务】SpringBoot+@Schedule注解

目录

一、SpringBoot+@Scheduled注解实现定时任务

1-1启用定时任务功能

[1-2 添加定时任务](#1-2 添加定时任务)

[1-2-1 cron表达式](#1-2-1 cron表达式)

[1-2-2 固定间隔定时任务](#1-2-2 固定间隔定时任务)

[1-2-3 固定频率定时任务](#1-2-3 固定频率定时任务)

[1-3 并行执行定时任务](#1-3 并行执行定时任务)

[1-3-1 修改任务调度器 默认使用的线程池](#1-3-1 修改任务调度器 默认使用的线程池)

[1-3-2 交给异步线程池处理](#1-3-2 交给异步线程池处理)


一、SpringBoot+@Scheduled注解实现定时任务

当使用SpringBoot框架时,可以使用@Scheduled注解来实现定时任务

1-1启用定时任务功能

在启动类或者配置类上增加@EnableScheduling注解

java 复制代码
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);
  }
}

1-2 添加定时任务

@Schedule注解支持cron表达式、固定时间、固定频率三种调度方式

1-2-1 cron表达式

与Linux下定时任务用到的Cron表达式一样

java 复制代码
@Scheduled(cron = "0/1 * * * * * *")
public void mytimer(){
    System.out.println("hello world");
}

注意:

【1】cron表达式配置的任务如果执行超时,会从上一个任务结束的时间开始计算间隔

【2】定时器的任务方法不能有返回值

【3】实现类上要有组件的注解@Component,@Service,@Repository

1-2-2 固定间隔定时任务

下一次的任务执行时间是从上一次定时任务结束时间开始计算

java 复制代码
@Scheduled(fixedDelay = 2)
public void mytimer(){
    System.out.println("hello world");
}

1-2-3 固定频率定时任务

下一次的任务执行时间是从上一次定时任务开始时间开始计算

java 复制代码
@Scheduled(@Scheduled(fixedRate = 2000)
public void mytimer(){
    System.out.println("hello world");
}

1-3 并行执行定时任务

默认状态下,只有一个线程在执行定时任务。

当有多有定时任务时,是串行的,不是并行的!!!

如有两个定时任务,分别设置每两秒执行一次,效果会是这样

Timer1:第0秒执行第一次

Timer2:第2秒执行第一次

Timer1:第4秒执行第二次

Timer2:第6秒执行第二次

.....

这并不是我们想要的效果,所以必须配置定时任务并行执行

1-3-1 修改任务调度器 默认使用的线程池

添加一个configuration,实现SchedulingConfigurer接口就可以了

java 复制代码
@Configuration
public class ScheduleConfig implements SchedulingConfigurer{
​
  @Override
  public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
    taskRegistrar.setTaskScheduler(getTaskScheduler());
  }
​
  @Bean
  public TaskScheduler getTaskScheduler() {
    ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
    taskScheduler.setPoolSize(3);  // 此处为线程池数量
    taskScheduler.setThreadNamePrefix("myworker-");
    taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
    return taskScheduler;
  }
}

1-3-2 交给异步线程池处理

启用@EnableAsync注解,并在每一个定时任务方法上使用@Async注解

java 复制代码
@Component
@EnableScheduling
@EnableAsync
public class MyCronTask {
​
  private static final Logger logger = LoggerFactory.getLogger(MyCronTask.class);
​
  @Async
  @Scheduled(fixedDelay = 2000)
  void task1Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task1 execute");
  }

  ​@Async
  @Scheduled(fixedDelay = 2000)
  void task2Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task2 execute");
  }
  
  ​@Async
  @Scheduled(fixedDelay = 2000)
  void task3Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task3 execute");
  }
}

@Async注解在使用时,如果不指定线程池的名称,则使用Spring默认的线程池,Spring默认的线程池为SimpleAsyncTaskExecutor

这个线程池的配置是

java 复制代码
    默认核心线程数:8,
    
    最大线程数:Integet.MAX_VALUE,
    队列使用LinkedBlockingQueue,
    容量是:Integet.MAX_VALUE,
    空闲线程保留时间:60s,
    线程池拒绝策略:AbortPolicy。

可以看到,最大线程数使用的是Integer.MAX_VALUE,即对于每一次定时任务的执行都会创建新的线程,并发环境下,会无限创建进程 -> OOM -> 系统崩溃

为了更好控制线程的使用,我们可以自定义线程池。

首先定义一个线程池

java 复制代码
@Configuration
public class MyTaskExecutor {
​
  @Bean(name = "myExecutor")
  public TaskExecutor getMyExecutor() {
    ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
    taskExecutor.setCorePoolSize(3);
    taskExecutor.setMaxPoolSize(10);
    taskExecutor.setQueueCapacity(20);
    taskExecutor.setThreadNamePrefix("myExecutor-");
    taskExecutor.initialize();
    return taskExecutor;
  }
}

然后在使用**@Async**注解时指定线程池

java 复制代码
@Component
@EnableScheduling
@EnableAsync
public class MyCronTask {
​
  private static final Logger logger = LoggerFactory.getLogger(MyCronTask.class);
​
  @Async("myExecutor")
  @Scheduled(fixedDelay = 2000)
  void task1Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task1 execute");
  }

  @Async("myExecutor")
  @Scheduled(fixedDelay = 2000)
  void task2Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task2 execute");
  }
  
  @Async("myExecutor")
  @Scheduled(fixedDelay = 2000)
  void task3Schedule() throws Exception{
    Thread.sleep(2000);
    logger.info("task3 execute");
  }
}
相关推荐
元亓亓亓7 分钟前
Java后端开发day46--多线程(二)
java·开发语言
七七小报13 分钟前
uniapp-商城-51-后台 商家信息(logo处理)
java·服务器·windows·uni-app
神奇小永哥17 分钟前
浅谈装饰模式
java
jiunian_cn23 分钟前
【c++】多态详解
java·开发语言·数据结构·c++·visual studio
yousuotu43 分钟前
python如何提取Chrome中的保存的网站登录用户名密码?
java·chrome·python
Code哈哈笑1 小时前
【图书管理系统】深度讲解:图书列表展示的后端实现、高内聚低耦合的应用、前端代码讲解
java·前端·数据库·spring boot·后端
郝开1 小时前
Java启动和停止jar文件sh脚本:自适应文件名方式启停 + 写死环境 启动;自适应文件名方式 + 命令行传参切换环境 启动
java·运维·jar
forestsea1 小时前
Maven 插件配置分层架构深度解析
java·架构·maven
JAVA坚守者1 小时前
深度解析 MySQL 与 Spring Boot 长耗时进程:从故障现象到根治方案(含 Tomcat 重启必要性分析)
spring boot·mysql·事务管理·连接池优化·数据库故障·慢查询治理·tomcat 运维
无名之逆1 小时前
Hyperlane: Unleash the Power of Rust for High-Performance Web Services
java·开发语言·前端·后端·http·rust·web