【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");
  }
}
相关推荐
金銀銅鐵1 小时前
[Java] 用 GUI 展示 EnumSet 增减元素时的内部变化(上)
java·后端·ai编程
福建佰胜张工1 小时前
SMC 薄型气爪 MHF2-16D1 深度解析:平行开闭、低剖面结构在自动化装配中的选型与落地
java·服务器·安全·自动化
catoop1 小时前
API Key 设计与安全生成方案:结构拆解、数据库设计与 Java 实现
java·数据库·安全
system_error_x2 小时前
SpringBoot 集成 Thymeleaf 简洁入门
java·spring boot·后端
驭渊的小故事2 小时前
Spring AI Alibaba 实战:从maven依赖治理到声明式工具调用
java·人工智能·spring
Elastic 中国社区官方博客3 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1883 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn4 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
y1su4 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
我命由我123455 小时前
Python 开发 - Python 的 pathlib
java·开发语言·后端·python·学习·java-ee·学习方法