Stage 11: Spring Boot, lesson 8 of 8

Scheduled and background tasks

Intermediate3 min read@since 17Code runs on your Java 25
Explain it forThe essentials plus production detail and pitfalls.

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

Java
@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

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.