Scheduled jobs and @Async methods

Spring Boot · lesson 23 of 39 · 4 min read

Run work on a timer or off the request thread, with pools you sized on purpose.

Open this lesson in the learning hub

Key points

  • Add @EnableScheduling, then @Scheduled on a no-argument method of a bean.
  • fixedDelay counts from the end of the previous run; fixedRate counts from its start and can overlap.
  • The scheduler pool is one thread by default - set spring.task.scheduling.pool.size or one slow job stalls the rest.
  • @Async plus @EnableAsync returns straight away and runs the body on the task executor.
  • An exception thrown in a void async method is lost unless you register an AsyncUncaughtExceptionHandler.
  • Several instances means several schedulers. Use ShedLock or a database lock so a job runs once per cluster.

Example

@Configuration
@EnableScheduling
@EnableAsync
class TaskConfig { }

@Component
class Housekeeping {

    @Scheduled(fixedDelay = 5000)          // 5s AFTER the previous run finished
    void purgeExpiredCarts() { carts.deleteExpired(); }

    @Scheduled(cron = "0 15 3 * * *", zone = "Europe/Berlin")   // 03:15 every day
    void nightlyReport() { reports.build(); }
}

@Service
class MailService {

    @Async                                  // caller returns immediately
    public CompletableFuture<Void> sendReceipt(Order order) {
        mailer.send(order.customerEmail(), render(order));
        return CompletableFuture.completedFuture(null);
    }
}

// application.yml
//   spring.task.scheduling.pool.size: 4
//   spring.task.execution.pool.max-size: 16
//   spring.task.execution.pool.queue-capacity: 100

Background work needs an explicit pool size and an explicit failure path, or it fails silently.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Spring Boot course, and every lesson in it is listed on the Spring Boot contents page.