抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文介绍了Spring的定时任务。

环境

Windows 10 企业版 LTSC 21H2
Java 1.8
Maven 3.6.3
Spring 5.3.23

1 配置依赖

添加依赖:

pom.xml
1
2
3
4
5
6
<!-- Spring Context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>

2 配置方式

2.1 编程式定时任务

编程式定时任务被封装成ScheduledFuture对象,不被Spring容器管理,Spring容器关闭时需要等待定时任务执行结束。

2.1.1 XML配置

创建任务类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class DemoTask {
// 简单任务
public void simpleTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行简单任务");
}
// 克戎任务
public void cronTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行克戎任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 固定速率任务
public void fixedRateTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行固定速率任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 固定延迟任务
public void fixedDelayTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行固定延迟任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

创建配置文件:

spring.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 任务对象 -->
<bean id="demoTask" class="com.example.task.DemoTask"/>

<!-- 任务调度器 -->
<bean id="taskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<!-- 核心线程数 -->
<property name="poolSize" value="5"/>
<!-- 线程名称前缀 -->
<property name="threadNamePrefix" value="scheduled-task-"/>
<!-- 线程池关闭时等待所有任务完成 -->
<property name="waitForTasksToCompleteOnShutdown" value="true"/>
<!-- 线程池关闭时等待所有任务完成超时时间,单位秒 -->
<property name="awaitTerminationSeconds" value="60"/>
</bean>
</beans>

创建启动类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class DemoApplication {
public static void main(String[] args) {
// 执行主任务
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行主任务");
// 加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 获取任务对象
DemoTask demoTask = context.getBean(DemoTask.class);
// 获取任务调度器
ThreadPoolTaskScheduler taskScheduler = context.getBean(ThreadPoolTaskScheduler.class);
// 执行简单任务,不立即执行,等待1秒后执行,只执行一次
taskScheduler.schedule(demoTask::simpleTask, new Date(System.currentTimeMillis() + 1000));
// 执行克戎任务,不立即执行,任务冲突等待下次触发时执行,实际间隔为4秒
taskScheduler.schedule(demoTask::cronTask, new CronTrigger("0/2 * * * * ?"));
// 执行固定速率任务,立即执行,任务冲突等待任务结束后立即执行,实际间隔为3秒
taskScheduler.scheduleAtFixedRate(demoTask::fixedRateTask, 2000);
// 执行固定延迟任务,立即执行,任务冲突等待任务结束后延迟执行,实际延迟为5秒
taskScheduler.scheduleWithFixedDelay(demoTask::fixedDelayTask, 2000);
// 等待10秒,观察任务执行情况
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 取消定时任务,关闭线程池
taskScheduler.shutdown();
System.out.println("取消定时任务");
// 等待5秒,观察任务是否继续执行
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 关闭容器
context.close();
}
}

2.1.2 注解配置

修改任务类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
@Component
public class DemoTask {
// 简单任务
public void simpleTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行简单任务");
}
// 克戎任务
public void cronTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行克戎任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 固定速率任务
public void fixedRateTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行固定速率任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 固定延迟任务
public void fixedDelayTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行固定延迟任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

创建配置类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Configuration
@ComponentScan("com.example")
public class DemoConfig {
// 任务调度器
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
// 创建任务调度器
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
// 核心线程数
scheduler.setPoolSize(5);
// 线程名称前缀
scheduler.setThreadNamePrefix("scheduled-task-");
// 线程池关闭时等待所有任务完成
scheduler.setWaitForTasksToCompleteOnShutdown(true);
// 线程池关闭时等待所有任务完成超时时间,单位秒
scheduler.setAwaitTerminationSeconds(60);
// 初始化任务调度器
scheduler.initialize();
// 返回任务调度器
return scheduler;
}
}

修改启动类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class DemoApplication {
public static void main(String[] args) {
// 执行主任务
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行主任务");
// 加载配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
// 获取任务对象
DemoTask demoTask = context.getBean(DemoTask.class);
// 获取任务调度器
ThreadPoolTaskScheduler taskScheduler = context.getBean(ThreadPoolTaskScheduler.class);
// 执行简单任务,不立即执行,等待1秒后执行,只执行一次
taskScheduler.schedule(demoTask::simpleTask, new Date(System.currentTimeMillis() + 1000));
// 执行克戎任务,不立即执行,任务冲突等待下次触发时执行,实际间隔为4秒
taskScheduler.schedule(demoTask::cronTask, new CronTrigger("0/2 * * * * ?"));
// 执行固定速率任务,立即执行,任务冲突等待任务结束后立即执行,实际间隔为3秒
taskScheduler.scheduleAtFixedRate(demoTask::fixedRateTask, 2000);
// 执行固定延迟任务,立即执行,任务冲突等待任务结束后延迟执行,实际延迟为5秒
taskScheduler.scheduleWithFixedDelay(demoTask::fixedDelayTask, 2000);
// 等待10秒,观察任务执行情况
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 取消定时任务,关闭线程池
taskScheduler.shutdown();
System.out.println("取消定时任务");
// 等待5秒,观察任务是否继续执行
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 关闭容器
context.close();
}
}

2.2 声明式定时任务

声明式定时任务被Spring容器管理,Spring容器关闭时不会等待定时任务执行结束。

2.2.1 XML配置

spring.xml配置文件中启用任务调度,需要添加task命名空间。

创建任务类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class DemoTask {
// 简单任务
public void simpleTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行简单任务");
}
// 克戎任务
public void cronTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行克戎任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 固定速率任务
public void fixedRateTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行固定速率任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 固定延迟任务
public void fixedDelayTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行固定延迟任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

创建配置文件:

spring.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!-- 任务对象 -->
<bean id="demoTask" class="com.example.task.DemoTask"/>

<!-- 任务调度器,默认线程池关闭时不会等待所有任务完成 -->
<task:scheduler id="taskScheduler" pool-size="5"/>

<!-- 配置定时任务 -->
<task:scheduled-tasks scheduler="taskScheduler">
<task:scheduled ref="demoTask" method="cronTask" cron="0/2 * * * * ?"/>
<task:scheduled ref="demoTask" method="fixedRateTask" fixed-rate="2000"/>
<task:scheduled ref="demoTask" method="fixedDelayTask" fixed-delay="2000"/>
</task:scheduled-tasks>
</beans>

创建启动类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DemoApplication {
public static void main(String[] args) {
// 执行主任务
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行主任务");
// 加载配置文件
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
// 获取任务调度器
ThreadPoolTaskScheduler taskScheduler = context.getBean(ThreadPoolTaskScheduler.class);
// 等待10秒,观察任务执行情况
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 取消定时任务,关闭线程池
taskScheduler.shutdown();
System.out.println("取消定时任务");
// 等待5秒,观察任务是否继续执行
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 关闭容器
context.close();
}
}

2.2.2 半注解配置

在XML配置文件中使用task:annotation-driven标签启用任务调度注解,支持通过@Scheduled注解完成声明式定时任务的配置,代替在XML配置文件中配置定时任务。

使用@Scheduled注解管理定时任务,可以在方法上使用,这是声明式定时任务最常用的方式。

修改任务类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Component
public class DemoTask {
// 克戎任务
@Scheduled(cron = "0/2 * * * * ?")
public void cronTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行克戎任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 固定速率任务
@Scheduled(fixedRate = 2000)
public void fixedRateTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行固定速率任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
// 固定延迟任务
@Scheduled(fixedDelay = 2000)
public void fixedDelayTask() {
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行固定延迟任务");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}

修改配置文件:

spring.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">
<!-- 启用注解扫描 -->
<context:component-scan base-package="com.example"/>

<!-- 任务调度器 -->
<bean id="taskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<!-- 核心线程数 -->
<property name="poolSize" value="5"/>
<!-- 线程名称前缀 -->
<property name="threadNamePrefix" value="scheduled-task-"/>
<!-- 线程池关闭时等待所有任务完成 -->
<property name="waitForTasksToCompleteOnShutdown" value="true"/>
<!-- 线程池关闭时等待所有任务完成超时时间,单位秒 -->
<property name="awaitTerminationSeconds" value="60"/>
</bean>

<!-- 启用任务调度注解 -->
<task:annotation-driven scheduler="taskScheduler"/>
</beans>

2.2.3 全注解配置

使用@EnableScheduling注解启用任务调度注解,代替在XML配置文件中使用task:annotation-driven标签。

创建配置类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Configuration
@EnableScheduling
@ComponentScan("com.example")
public class DemoConfig {
// 任务调度器
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
// 创建任务调度器
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
// 核心线程数
scheduler.setPoolSize(5);
// 线程名称前缀
scheduler.setThreadNamePrefix("scheduled-task-");
// 线程池关闭时等待所有任务完成
scheduler.setWaitForTasksToCompleteOnShutdown(true);
// 线程池关闭时等待所有任务完成超时时间,单位秒
scheduler.setAwaitTerminationSeconds(60);
// 初始化任务调度器
scheduler.initialize();
// 返回任务调度器
return scheduler;
}
}

修改启动类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class DemoApplication {
public static void main(String[] args) {
// 执行主任务
System.out.println(LocalDateTime.now() + " " + Thread.currentThread().getName() + " 执行主任务");
// 加载配置类
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
// 获取任务调度器
ThreadPoolTaskScheduler taskScheduler = context.getBean(ThreadPoolTaskScheduler.class);
// 等待10秒,观察任务执行情况
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 取消定时任务,关闭线程池
taskScheduler.shutdown();
System.out.println("取消定时任务");
// 等待5秒,观察任务是否继续执行
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 关闭容器
context.close();
}
}

3 使用说明

定时任务使用TaskScheduler接口的ThreadPoolTaskScheduler主要实现类,它基于线程池提供了灵活的定时任务调度功能。

常用方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 执行定时任务,从指定开始时间开始执行
public ScheduledFuture<?> schedule(Runnable task, Date startTime);
// 执行定时任务,指定触发规则
public ScheduledFuture<?> schedule(Runnable task, Trigger trigger);
// 执行定时任务,指定固定时间间隔
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long period);
// 执行定时任务,指定固定时间间隔,从指定开始时间开始执行
public ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Date startTime, long period);
// 执行定时任务,指定固定延迟时间
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long delay);
// 执行定时任务,指定固定延迟时间,从指定开始时间开始执行
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Date startTime, long delay);
// 关闭全部定时任务
public void shutdown();

获取ScheduledFuture定时任务后支持关闭单个任务:

java
1
2
// 关闭单个定时任务,设置是否允许中断运行中的任务,返回是否成功
boolean cancel(boolean mayInterruptIfRunning);

4 克戎表达式

克戎表达式即Cron表达式,用于定义定时任务的执行规则。

克戎表达式由七个时间字段构成,顺序如下:

  • 秒:取值范围在0-59之间,可使用, - * /特殊字符。
  • 分:取值范围在0-59之间,可使用, - * /特殊字符。
  • 时:取值范围在0-23之间,可使用, - * /特殊字符。
  • 日:取值范围在1-31之间,可使用, - * ? /特殊字符。
  • 月:取值范围在1-12之间,可使用, - * /特殊字符。
  • 星期:取值范围在0-6之间,从星期日开始,可使用, - * ? /特殊字符。
  • 年(可选):取值范围在1970-2099之间,可使用, - * /特殊字符。

特殊字符的含义:

  • *:表示任意值,例如*表示匹配字段的任意值。
  • ,:表示枚举值,例如1,2,3表示匹配指定的多个值。
  • -:表示范围,例如1-3表示匹配范围内的连续值。
  • /:表示增量,例如0/5表示从0开始,每5分钟执行一次。
  • ?:表示不指定值,只能用在日和星期中,当一个值为具体值时,另一个值必须为?避免冲突。

示例:

cron
1
2
3
4
5
0 0 8 * * ? - 每天8点执行
0 10 30 * * ? - 每天10:30执行
0 0/5 * * * ? - 每天每5分钟执行
0 0/5 12 * * ? - 每天12点到13点期间,每5分钟执行
0 0-5 12 * * ? - 每天12点到12:05期间,每1分钟执行

评论