Spring: Task Scheduling with @Scheduled
Spring's scheduling support lets you run methods on a fixed schedule (every N milliseconds, after a delay, or using a cron expression) without adding a third-party scheduler library. All you need is @EnableScheduling and @Scheduled.
Contents
@EnableScheduling
Add @EnableScheduling@Configuration@SpringBootApplication
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
@Configuration
@EnableScheduling
public class SchedulingConfig {
// Optionally define a TaskScheduler bean here
}
ThreadPoolTaskScheduler@Scheduled with fixedRate / fixedDelay / cron
@Scheduled
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class ReportScheduler {
// fixedRate β run every 5 seconds measured from the START of the previous execution
@Scheduled(fixedRate = 5000)
public void generateReport() {
System.out.println("Report generated at " + Instant.now());
}
// fixedDelay β wait 5 seconds AFTER the previous execution FINISHES before running again
@Scheduled(fixedDelay = 5000)
public void cleanUpTempFiles() {
System.out.println("Cleanup done at " + Instant.now());
}
// cron β run at 2:30 AM every day
@Scheduled(cron = "0 30 2 * * ?")
public void dailyBackup() {
System.out.println("Daily backup started at " + Instant.now());
}
// Use ISO duration strings (Spring 5.1+) β cleaner than raw ms
@Scheduled(fixedRateString = "PT10S") // every 10 seconds
public void pingHealthCheck() { }
@Scheduled(fixedDelayString = "PT1M") // 1 minute after last run
public void syncData() { }
// Read from application.properties
@Scheduled(cron = "${reports.schedule.cron:0 0 6 * * ?}")
public void configuredJob() { }
}
| Mode | Timing measured from | Overlap risk |
|---|---|---|
fixedRate | Start of previous execution | Yes: if task takes longer than the rate |
fixedDelay | End of previous execution | No: always waits for previous to finish |
cron | Calendar time | No (single thread): can pile up with thread pool |
Cron Expression Syntax
Spring uses a 6-field cron expression: second minute hour day-of-month month day-of-week
| Field | Allowed values | Special chars |
|---|---|---|
| Second | 0β59 | , - * / |
| Minute | 0β59 | , - * / |
| Hour | 0β23 | , - * / |
| Day of month | 1β31 | , - * ? / L W |
| Month | 1β12 or JANβDEC | , - * / |
| Day of week | 0β7 (0,7=Sun) or SUNβSAT | , - * ? / L # |
// Every minute
@Scheduled(cron = "0 * * * * ?")
// Every day at midnight
@Scheduled(cron = "0 0 0 * * ?")
// Every weekday at 9 AM
@Scheduled(cron = "0 0 9 * * MON-FRI")
// Every 30 minutes
@Scheduled(cron = "0 0/30 * * * ?")
// First day of every month at 6 AM
@Scheduled(cron = "0 0 6 1 * ?")
// Every Sunday at 3 AM
@Scheduled(cron = "0 0 3 ? * SUN")
@Scheduled with initialDelay
initialDelay
@Component
public class WarmupTask {
// Wait 30 seconds after startup, then run every 10 minutes
@Scheduled(initialDelay = 30_000, fixedRate = 600_000)
public void refreshCache() {
cacheService.refresh();
}
// ISO duration strings
@Scheduled(initialDelayString = "PT30S", fixedRateString = "PT10M")
public void pollExternalApi() {
apiClient.poll();
}
// From properties
@Scheduled(initialDelayString = "${scheduler.initial-delay:PT1M}", fixedDelayString = "PT5M")
public void syncData() {
dataSync.run();
}
}
Dynamic Scheduling with TaskScheduler
For schedules that are determined at runtime (e.g., from a database), inject a TaskScheduler
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service;
import java.util.concurrent.ScheduledFuture;
@Service
public class DynamicSchedulerService {
private final TaskScheduler taskScheduler;
private ScheduledFuture<?> scheduledTask;
public DynamicSchedulerService(TaskScheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
public void scheduleTask(String cronExpression) {
// Cancel existing task if running
if (scheduledTask != null) {
scheduledTask.cancel(false);
}
scheduledTask = taskScheduler.schedule(
this::doWork,
new CronTrigger(cronExpression)
);
}
public void scheduleFixedRate(long intervalMs) {
if (scheduledTask != null) {
scheduledTask.cancel(false);
}
scheduledTask = taskScheduler.scheduleAtFixedRate(
this::doWork,
Duration.ofMillis(intervalMs)
);
}
public void cancelTask() {
if (scheduledTask != null) {
scheduledTask.cancel(false);
}
}
private void doWork() {
System.out.println("Dynamic task executed at " + Instant.now());
}
}
ThreadPoolTaskScheduler
The default single-threaded scheduler blocks concurrent task execution.
Configure a ThreadPoolTaskScheduler
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableScheduling
public class SchedulingConfig {
@Bean
public ThreadPoolTaskScheduler taskScheduler() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(5); // 5 concurrent tasks
scheduler.setThreadNamePrefix("scheduled-task-");
scheduler.setAwaitTerminationSeconds(60);
scheduler.setWaitForTasksToCompleteOnShutdown(true);
return scheduler;
}
}
fixedDelayfixedRate