Async, Scheduling, Virtual Threads — Java Interview Guide | Cracked Java
Senior

Async, Scheduling, Virtual Threads

@Async and its executor, async return types, @Scheduled with fixedRate/fixedDelay/cron, distributed scheduling, virtual threads in Boot 3.2+, and when they help.

Prereqs: aop-proxies

Spring lets you run work off the calling thread — asynchronously with @Async, or on a clock with @Scheduled — and both are built on the same AOP-proxy + TaskExecutor machinery you already know. @Async says "run this method on some other thread and return immediately"; @Scheduled says "the framework calls this method for me, on a fixed rate, a fixed delay, or a cron expression." Neither requires you to touch Thread or ExecutorService directly — you annotate a bean method and Spring wires the execution.

Because both ride on proxies, the same traps reappear: a method only becomes async/scheduled when invoked through the proxy, so self-invocation (this.foo()) silently runs it inline on the caller's thread.

@Service
public class ReportService {
    @Async
    public CompletableFuture<Report> generate(long id) {  // runs on a TaskExecutor thread
        return CompletableFuture.completedFuture(build(id));
    }

    @Scheduled(fixedDelay = 60_000)
    public void purgeStale() { ... }                       // framework calls this every 60s
}

Enable them with @EnableAsync and @EnableScheduling (Boot turns scheduling on when it sees @Scheduled). The executor behind @Async in Boot 3 is the auto-configured applicationTaskExecutor; the scheduler behind @Scheduled is single-threaded by default — a long task blocks every other scheduled task.

The 2024 plot twist is virtual threads (Project Loom, JDK 21). Setting spring.threads.virtual.enabled=true switches Tomcat request handling and the async/scheduling executors to virtual threads, so blocking IO work scales to thousands of concurrent tasks cheaply — without rewriting anything as reactive. The catch: virtual threads help IO-bound/blocking code, not CPU-bound work, and synchronized blocks still pin the carrier thread before JDK 24.

The questions below cover how @Async works and its return types, the self-invocation trap, fixedRate/fixedDelay/cron, distributed scheduling with ShedLock, what virtual threads actually switch, when they help, and ThreadPoolTaskExecutor tuning.

Questions

8 in this topic