文章目录
1. 定时任务方案
在项目中我们经常需要使用定时任务,实现定时任务的方案主要如下:
-
使用 JDK 的 Timer 和 TimerTask 实现
可以实现简单的间隔执行任务,无法实现按日历去调度执行任务。
-
使用 Quartz 实现
Quartz 是一个异步任务调度框架,功能丰富,可以实现按日历调度。
-
使用 Spring Task 实现
Spring 3.0 后提供 Spring Task 实现任务调度,支持按日历调度,相比 Quartz 功能稍简单,但是在开发基本够用,支持注解编程方式。
2. Spring Task 串行任务
2.1 编写任务类
实现步骤:
- 创建任务类,并添加 @Component 注解
- 创建任务方法,并添加 @Scheduled 注解,指定任务执行策略
- 在 SpringBoot 启动类上添加注解:@EnableScheduling,开启定时任务
代码:
@Component
public class MyTask {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
@Scheduled(cron = "0/5 * * * * *") // 每隔 5 秒执行一次
public void task1() {
System.out.println("定时任务1开始:" + sdf.format(System.currentTimeMillis()));
try {
Thread.sleep(3000); // 定时任务执行 3 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("定时任务1结束:" + sdf.format(System.currentTimeMillis()));
}
}
运行结果:
定时任务1开始:2020/10/16 05:03:40
定时任务1结束:2020/10/16 05:03:43
定时任务1开始:2020/10/16 05:03:45
定时任务1结束:2020/10/16 05:03:48
定时任务1开始:2020/10/16 05:03:50
定时任务1结束:2020/10/16 05:03:53
...
2.2 串行任务测试
参考 task1 方法的的定义方法,再定义 task2 方法,此时共用两个任务方法。
代码:
@Component
public class MyTask {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss");
@Scheduled(cron = "0/5 * * * * *") //每隔 5 秒执行一次
public void task1() {
System.out.println("定时任务1开始:" + sdf.format(System.currentTimeMillis()));
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("定时任务1结束:" + sdf.format(System.currentTimeMillis()));
}
@Scheduled(cron = "0/5 * * * * *") //每隔 5 秒执行一次
public void task2() {
System.out.println("定时任务2开始:" + sdf.format(System.currentTimeMillis()));
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("定时任务2结束:" + sdf.format(System.currentTimeMillis()));
}
}
运行结果:
定时任务1开始:2020/10/16 05:11:00
定时任务1结束:2020/10/16 05:11:03
定时任务2开始:2020/10/16 05:11:03
定时任务2结束:2020/10/16 05:11:06
定时任务1开始:2020/10/16 05:11:06
定时任务1结束:2020/10/16 05:11:09
定时任务1开始:2020/10/16 05:11:10
定时任务1结束:2020/10/16 05:11:13
定时任务2开始:2020/10/16 05:11:13
定时任务2结束:2020/10/16 05:11:16
...
2.3 @Scheduled 所支持的参数
- cron:cron 表达式,指定任务在特定时间执行
- fixedDelay:表示上一次任务执行完成后多久再次执行,参数类型为 long,单位 ms
- fixedDelayString:与 fixedDelay 含义一样,只是参数类型变为 String
- fixedRate:表示上一次任务执行开始后多久再次执行,参数类型为 long,单位 ms
- fixedRateString: 与 fixedRate 的含义一样,只是将参数类型变为 String
- initialDelay:表示延迟多久再第一次执行任务,参数类型为 long,单位 ms
- initialDelayString:与 initialDelay 的含义一样,只是将参数类型变为 String
- zone:时区,默认为当前时区,一般没有用到
2.4 cron 表达式
cron 表达式包括 6 部分:
- 秒(0~59)
- 分钟(0~59)
- 小时(0~23)
- 月中的天(1~31)
- 月(1~12)
- 周中的天(填写 1~7,或 Mon、TUE 等)
特殊字符介绍:
- “/” 字符表示指定数值的增量
- “*” 字符表示所有可能的值
- “-” 字符表示区间范围
- “,” 字符表示列举
- “?” 字符仅被用于月中的天和周中的天两个子表达式,表示不指定值(也就是月中的天和周中的天只能保留一个,另一个用 ? 表示不指定值)
例子:
- 0/3 * * * * * 每隔3秒执行
- 0 0/5 * * * * 每隔5分钟执行
- 0 0 0 * * * 表示每天0点执行
- 0 0 12 ? * WEN 每周三12点执行
- 0 15 10 ? * MON-FRI 每月的周一到周五10点15分执行
- 0 15 10 ? * MON,FRI 每月的周一和周五10点15分执行
3. Spring Task 并行任务
Spring Task 默认是单线程的,上面的两个定时任务使用的都是同一个线程,所以会串行执行任务。但有时我们也需要并行执行多个不同的任务,这时就需要使用多线程技术了,我们可以配置线程池来实现多线程调度任务。
代码:
-
创建配置类,创建线程池对象并将其加入 IOC 容器
@Configuration @EnableScheduling public class ScheduleConfig { @Bean public ThreadPoolTaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.initialize(); //初始化线程池 scheduler.setPoolSize(2); //线程池容量 return scheduler; } }
-
任务类不变
@Component public class MyTask { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd hh:mm:ss"); @Scheduled(cron = "0/5 * * * * *") //每隔 5 秒执行一次 public void task1() { System.out.println("定时任务1开始:" + sdf.format(System.currentTimeMillis())); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("定时任务1结束:" + sdf.format(System.currentTimeMillis())); } @Scheduled(cron = "0/5 * * * * *") //每隔 5 秒执行一次 public void task2() { System.out.println("定时任务2开始:" + sdf.format(System.currentTimeMillis())); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("定时任务2结束:" + sdf.format(System.currentTimeMillis())); } }
-
将 @EnableScheduling 添加到此配置类上,SpringBoot 启动类上不用再添加 @EnableScheduling
运行结果:
定时任务2开始:2020/10/16 11:52:10
定时任务1开始:2020/10/16 11:52:10
定时任务1结束:2020/10/16 11:52:13
定时任务2结束:2020/10/16 11:52:13
定时任务2开始:2020/10/16 11:52:15
定时任务1开始:2020/10/16 11:52:15
...