Scheduled and background tasks
Scheduling runs methods on a timer:
- Turn it on with
@EnableScheduling. @Scheduled(fixedRate = 60000): every minute, measured from each start.@Scheduled(fixedDelay = …): waits a fixed time after each run finishes.@Scheduled(cron = "0 0 2 * * *", zone = "Asia/Kolkata"): at 2:00 every day. Spring cron has six fields and starts with seconds.
@Async runs a method on a background thread and returns immediately (void or CompletableFuture):
- Turn it on with
@EnableAsync, and configure a bounded thread pool or virtual threads. - Use it for slow side work: emails, reports, webhook calls.
When you run several instances, every instance runs every schedule. Use a lock (ShedLock) or a dedicated worker so a nightly job runs once, not once per server.
Example
@Configuration
@EnableScheduling
@EnableAsync
class JobsConfig {}
@Component
public class Jobs {
private static final Logger log = LoggerFactory.getLogger(Jobs.class);
private final OrderRepository orders;
Jobs(OrderRepository orders) { this.orders = orders; }
@Scheduled(cron = "0 0 2 * * *", zone = "Asia/Kolkata") // every day at 2:00 IST
public void expireAbandonedOrders() {
int n = orders.expireOlderThan(Instant.now().minus(Duration.ofDays(1)));
log.info("Expired {} abandoned orders", n);
}
@Scheduled(fixedDelay = 30_000) // 30 s after the last run ends
public void retryFailedWebhooks() { /* ... */ }
}
@Service
class Mailer {
@Async
public CompletableFuture<Void> sendReceipt(String email) { // runs on a background thread
// ... slow SMTP call
return CompletableFuture.completedFuture(null);
}
}Common mistake
Running a scheduled job on every instance after scaling out. Add a distributed lock such as ShedLock, or run jobs in a single worker.
Under the hood
By default all @Scheduled methods share one thread, so a slow job delays the others; raise spring.task.scheduling.pool.size or turn on virtual threads. Exceptions in @Async void methods are only logged; return a CompletableFuture or configure an AsyncUncaughtExceptionHandler. For work that must never be lost (payments, important emails), use a queue or a jobs table rather than in-memory async calls.
Check yourself
How many fields does a Spring cron expression have?
How this connects
Know these first
Where this leads
You've reached the end of this thread. Try a learning path for what's next.
Part of Microservices and production.
Was this lesson helpful?
Finished reading? Mark it complete to track your progress.