Spring中的定时任务
@Scheduled 注解
在 Spring 中,@Scheduled 注解用来配置和安排任务。使用它标注的方法应当满足两点:
- 方法没有返回值(void),即使有也会被忽略
- 方法不应该有任何参数
启用 Scheduled
@Configuration
@EnableScheduling
public class SpringConfig {
...
}
按固定延迟安排任务
@Scheduled(fixedDelay = 1000)
public void scheduleFixedDelayTask() {
System.out.println(
"Fixed delay task - " + System.currentTimeMillis() / 1000);
}
在这种情况下,上一次执行结束和下一次执行开始之间的时间是固定的。该任务总是等待,直到前一个任务完成。
当强制要求在再一次运行前需要完成前一次的执行时,应该使用这个选项。
按固定速率计划任务
@Scheduled(fixedRate = 1000)
public void scheduleFixedRateTask() {
System.out.println(
"Fixed rate task - " + System.currentTimeMillis() / 1000);
}
当每个任务的执行都是独立的时候,应该使用这个选项。
请注意,计划任务在默认情况下不是并行的。因此,即使我们使用 fixedRate,在前一个任务完成之前,下一个任务也不会被调用。
如果我们想在计划任务中支持并行行为,我们需要添加@Async注解:
@EnableAsync
public class ScheduledFixedRateExample {
@Async
@Scheduled(fixedRate = 1000)
public void scheduleFixedRateTaskAsync() throws InterruptedException {
System.out.println(
"Fixed rate task async - " + System.currentTimeMillis() / 1000);
Thread.sleep(2000);
}
}
我们可以使用Spring的@Scheduled注解来运行一个计划任务,但是基于fixedDelay 和 fixedRate 属性,执行的性质会发生变化。
fixedDelay 属性可以确保在执行任务的结束时间和下一次执行任务的开始时间之间有一个n毫秒的延迟。
当我们需要确保一直只有一个任务的实例在运行时,这个属性特别有用。对于依赖性工作,它是相当有帮助的。
fixedRate 属性每隔 n 毫秒运行一次计划任务。它不检查任务的任何先前的执行情况。
当任务的所有执行都是独立的时候,这很有用。如果我们不期望超过内存和线程池的大小,fixedRate 应该是相当方便的。
不过,如果传入的任务不能迅速完成,它们有可能以 "内存不足的异常 "而告终。
并行运行任务
默认情况下,Spring 使用本地单线程调度器来运行任务。因此,即使我们有多个@Scheduled 方法,它们都需要等待线程完成对前一个任务的执行。
如果我们的任务是真正独立的,那么并行运行它们会更方便。为此,我们需要提供一个更适合我们需求的 TaskScheduler:
@Bean
public TaskScheduler taskScheduler() {
ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
threadPoolTaskScheduler.setThreadNamePrefix("ThreadPoolTaskScheduler");
return threadPoolTaskScheduler;
}
在上面的例子中,我们将 TaskScheduler 的池子大小配置为5个,但请记住,实际配置应该根据自己的具体需要进行微调。
如果我们使用 Spring Boot,我们可以利用一个更方便的方法来增加调度器的池大小。
只需设置spring.task.scheduling.pool.size属性即可:
spring.task.scheduling.pool.size=5
来源:The @Scheduled Annotation in Spring | Baeldung
Task Scheduler
TaskScheduler 是在 Spring 3.0 中引入的,它有多种方法可以在未来的某个时间点运行,它还会返回一个 ScheduledFuture<V> 接口的表示对象,可以用来取消scheduled task 或检查它是否已经完成。
All we need to do is to select a runnable task for scheduling then select a proper scheduling policy.
我们需要做的就是选择一个可运行的任务进行调度,然后选择一个合适的调度策略。
ThreadPoolTaskScheduler
ThreadPoolTaskScheduler非常适合内部线程管理,因为它将任务委托给ScheduledExecutorService并实现了TaskExecutor接口--所以它的单个实例能够处理异步的潜在执行以及@Scheduled注释。
@Configuration
@ComponentScan(
basePackages="com.baeldung.taskscheduler",
basePackageClasses={ThreadPoolTaskSchedulerExamples.class})
public class ThreadPoolTaskSchedulerConfig {
@Bean
public ThreadPoolTaskScheduler threadPoolTaskScheduler(){
ThreadPoolTaskScheduler threadPoolTaskScheduler
= new ThreadPoolTaskScheduler();
threadPoolTaskScheduler.setPoolSize(5);
threadPoolTaskScheduler.setThreadNamePrefix(
"ThreadPoolTaskScheduler");
return threadPoolTaskScheduler;
}
}
配置的Bean threadPoolTaskScheduler 可以根据配置的5个池的大小异步执行任务。
请注意,所有与 ThreadPoolTaskScheduler 相关的线程名称都将以ThreadPoolTaskScheduler 为前缀。
让我们实现一个简单的任务,然后我们可以安排:
class RunnableTask implements Runnable{
private String message;
public RunnableTask(String message){
this.message = message;
}
@Override
public void run() {
System.out.println(new Date()+" Runnable Task with "+message
+" on thread "+Thread.currentThread().getName());
}
}
我们现在可以简单地安排这个任务由调度器执行:
taskScheduler.schedule(
new Runnabletask("Specific time, 3 Seconds from now"),
new Date(System.currentTimeMillis + 3000)
);
taskScheduler 将在一个已知的日期安排这个可运行的任务,正好是当前时间的3秒后。
现在让我们更深入地了解一下 ThreadPoolTaskScheduler 的调度机制。
Schedule Runnable Task With Fixed Delay(固定延迟)
taskScheduler.scheduleWithFixedDelay(
new RunnableTask("Fixed 1 second Delay"), 1000);
taskScheduler.scheduleWithFixedDelay(
new RunnableTask("Current Date Fixed 1 second Delay"),
new Date(),
1000);
Scheduling at a Fixed Rate(固定速率)
taskScheduler.scheduleAtFixedRate(
new RunnableTask("Fixed Rate of 2 seconds") , 2000);
taskScheduler.scheduleAtFixedRate(new RunnableTask(
"Fixed Rate of 2 seconds"), new Date(), 3000);
来源:A Guide to the Spring Task Scheduler | Baeldung
ThreadPoolTaskScheduler与ThreadPoolTaskExecutor
- ThreadPoolTaskExecutor是一个专门用于执行任务的类。
- ThreadPoolTaskScheduler是一个专门用于调度任务的类。
一个ThreadPoolTaskExecutor通过它的corePoolSize , maxPoolSize , keepAliveSeconds和queueCapacity属性在线程池中提供细粒度的配置。 诸如ThreadPoolTaskScheduler这样的调度器不提供这样的配置。
因此,在两者之间进行选择归结为以下问题:是否需要执行或计划执行任务?根据不同的用途去选择就可以
来源:task - Spring ThreadPoolTaskScheduler vs ThreadPoolTaskExecutor - Stack Overflow