Ch. 8 · Spring Boot

Spring Boot Scheduled Tasks

Run recurring work with @Scheduled, coordinate across instances, and avoid overlap and long-running jobs.

~2 min readintermediateupdated Oct 5, 2026

@Scheduled runs a method on a fixed rate or cron expression, which is convenient for recurring work. In a multi-instance deployment, though, every instance runs the job, so coordination and overlap become the real concerns.

Before you start

You should be comfortable with beans and configuration. This article covers scheduling and its distributed pitfalls.

Step-by-step walkthrough

Step 1: Choose fixed rate or fixed delay

A fixed rate schedules the next run a set time after the previous start, which can overlap if the work is slow. A fixed delay waits a set time after the previous run finishes, which prevents overlap but drifts. Choose based on whether overlap is acceptable.

Step 2: Coordinate across instances

In a cluster, @Scheduled fires on every instance unless you guard it, so a job that sends emails would run N times. Use a distributed lock or a scheduler with leader election so only one instance runs the job.

Step 3: Keep jobs bounded and observable

Bound the work so a run finishes within its interval, log what it did, and handle errors so one failure does not silently stop the schedule. A job that throws repeatedly should be visible, not swallowed.

Worked scenario

A guard ensures only one instance runs the job.

@Scheduled(cron = "0 0 * * * *")   // hourly
public void sendDigests() {
  if (!lock.tryAcquire("digest-job", Duration.ofMinutes(5))) return;
  try { digestService.sendAll(); }
  finally { lock.release("digest-job"); }
}
java

Walk through the example

The cron expression runs the method hourly on every instance, but the lock means only the instance that acquires it proceeds; the others return immediately. That keeps the digest from being sent once per instance. The lock is released in a finally, so a failure does not leave it held.

Common mistake

Assuming a scheduled method runs once in a cluster, which it does not, so jobs duplicate work. Another is a fixed-rate job that takes longer than its interval and overlaps itself.

Verify the behavior

Run two instances and confirm the guarded job executes once. Time a job longer than its interval under a fixed rate and observe the overlap, then switch to fixed delay. Confirm errors are logged rather than swallowed.

Interview exercise

Why can @Scheduled duplicate work in a cluster?

Answer and reasoning

Because the annotation registers the schedule in each application context, and every instance has its own, so each fires the job. Without coordination, a job like sending emails runs once per instance. A distributed lock or a leader-elected scheduler ensures a single execution.

Continue learning

Compare coordination in Leader election and async work in Async executor. Read the Spring scheduling documentation and try the Spring Boot interview questions.

More in Spring Boot

read ✓Spring Boot · hard

Spring @Async and Executor Configuration

Run methods asynchronously with @Async, configure a bounded executor, and handle exceptions and the proxy boundary.

~2 min readread →
read ✓Spring Boot · hard

Spring Boot Caching Abstraction

Cache method results with @Cacheable, choose keys and TTLs, and evict on writes without the self-invocation trap.

~2 min readread →
read ✓Spring Boot · hard

Spring Declarative HTTP Clients

Define outbound HTTP as an annotated interface with @HttpExchange, create the proxy, and configure timeouts and errors.

~2 min readread →
esc