文章目录
- 前言
- 为什么选择Spring Schedule
- Cron表达式
- 简单示例
- 测试结果
- 优化方案
前言
Spring Schedule是Spring框架提供的一种简单的定时任务解决方案。它是基于Java的@Scheduled注解,可以让我们在不影响主线程的情况下,定时、周期性地执行任务。
为什么选择Spring Schedule
选择Spring Schedule作为定时任务的原因主要有以下几点:
- 简单易用:Spring Schedule的使用非常简单,只需要在方法上添加@Scheduled注解,就可以将该方法变为定时任务。
- 集成方便:如果你的项目已经是Spring框架,那么使用Spring Schedule不需要额外引入其他依赖,只需要简单的配置即可。
- 支持CRON表达式:Spring Schedule支持CRON表达式,这使得我们可以非常灵活地配置定时任务的执行时间。
- 支持异步执行:Spring Schedule支持异步执行任务,这意味着定时任务的执行不会阻塞主线程。
Cron表达式
Cron 表达式是一个字符串,由多个时间字段组成,用空格分隔。一个标准的 Cron 表达式有 6 个或 7 个字段,分别表示秒、分钟、小时、日期、月份、星期以及(可选的)年份。
- 秒(0-59)
- 分(0-59)
- 时(0-23)
- 日(1-31)
- 月(1-12 或 JAN-DEC)
- 周(1-7 或 SUN-SAT)
- 年(可选,留空表示任意年份)
除了上述基本的取值范围外,Cron 表达式还支持一些特殊字符,如:
*
:代表所有可能的值。例如,使用 * 表示每分钟、每小时或每月等。
/
:用于指定一个增量。例如,0/15 表示从 0 分钟开始,每隔 15 分钟执行一次。
-
:用于指定一个范围。例如,10-20 表示从 10 到 20。
,
:用于列举多个取值。例如,MON,WED,FRI 表示星期一、星期三和星期五。
?
:用于天或星期字段,表示无需指定具体值。
简单示例
这里我做了一个简单的示例:这里写了两个定时任务,同一个时间往数据库里面插入数据,每个任务插入1000条数据。
Application.class
@EnableScheduling
@SpringBootApplication
@MapperScan("org.example.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
这里记得要有@EnableScheduling
注解,才能使用定时任务。
ScheduledService.class
package org.example.service;
import org.example.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Service
public class ScheduledService {
@Autowired
UserService userService;
@Scheduled(cron = "0 08 10 * * ?")
public void scheduledMethod(){
System.out.println("第一个定时任务开始:"+ LocalDateTime.now());
List<User> list = new ArrayList<User>();
for(int i = 0;i<1000;i++){
User user = new User();
user.setId((long)i);
user.setName("keaizp");
user.setAge(25);
list.add(user);
}
for (User user : list) {
userService.insert(user);
}
System.out.println("第一个定时任务结束:"+ LocalDateTime.now());
}
@Scheduled(cron = "0 08 10 * * ?")
public void Method2(){
System.out.println("第二个定时任务开始:"+ LocalDateTime.now());
List<User> list = new ArrayList<User>();
for(int i = 1000;i<2000;i++){
User user = new User();
user.setId((long)i);
user.setName("keaizp");
user.setAge(25);
list.add(user);
}
for (User user : list) {
userService.insert(user);
}
System.out.println("第二个定时任务结束:"+ LocalDateTime.now());
}
}
测试结果
可以看到定时任务会出现积压现象,虽然两个定时任务是设置同一时间的,但是第二个定时任务先执行,等第二个定时任务执行完成,第一个定时任务才开始执行。两个定时任务一共用时3分48秒。
优化方案
可以考虑使用异步执行的方式,使得任务的执行不会阻塞任务调度的进程。Spring Schedule默认是单线程执行任务的,如果有多个任务需要并行执行,那么可能会出现任务执行的延迟。这种情况下,可以考虑使用Spring Schedule的并行任务执行功能,通过配置TaskScheduler来实现。
package org.example.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
private final int POOL_SIZE = 10;
@Override
public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
threadPoolTaskScheduler.initialize();
scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
}
}
运行结果可以看到两个任务是并行执行的,两个任务完成一共用时1分54秒,效率大大得到提升!