Spring AI
    July 16, 2026

    Virtual Threads in Spring Boot: What Changes and How to Use Them

    Java 21's virtual threads let Spring Boot handle massive concurrency for I/O-bound work without going reactive. How to enable them, when they help, and the pitfalls to avoid.

    Share

    For years, scaling a blocking Spring Boot app meant one of two painful choices: throw more threads (and memory) at it, or rewrite everything in a reactive style. Java 21's virtual threads (Project Loom) offer a third way — keep your simple, blocking, easy-to-debug code, and still handle tens of thousands of concurrent requests. Spring Boot makes turning them on almost trivial. Here's what actually changes.

    The problem virtual threads solve

    A traditional (platform) thread maps 1:1 to an OS thread — it's heavy (~1MB stack) and you can only have a few thousand before memory and scheduling overhead hurt. In a typical web app, most of those threads spend their time blocked waiting on I/O: a database query, an HTTP call, an LLM API response. You're paying for expensive threads to sit idle.

    Virtual threads are lightweight threads managed by the JVM, not the OS. You can have millions. When a virtual thread blocks on I/O, the JVM parks it and reuses the underlying carrier thread for other work — so blocking code scales like non-blocking code, without the non-blocking complexity.

    Turning them on in Spring Boot

    If you're on Spring Boot 3.2+ and Java 21+, it's a single property:

    spring:
      threads:
        virtual:
          enabled: true

    That's it. With this enabled, Spring Boot runs your web server (Tomcat/Jetty) request handling on virtual threads, and @Async tasks and other executors use them too. Your controllers, services, and blocking JDBC/HTTP calls stay exactly as they are.

    @RestController
    public class OrderController {
    
        private final OrderService orders;
    
        public OrderController(OrderService orders) {
            this.orders = orders;
        }
    
        // Blocking code — but now each request runs on a virtual thread,
        // so thousands of concurrent slow calls no longer exhaust the pool.
        @GetMapping("/orders/{id}")
        public Order get(@PathVariable String id) {
            return orders.fetchFromDbAndEnrich(id); // blocks on DB + an HTTP call
        }
    }
    NOTE

    Virtual threads need Java 21+. Confirm your runtime, not just your build target — running on Java 17 silently ignores the feature.

    Why this matters for AI apps specifically

    LLM calls are the ultimate blocking I/O: a single request can wait seconds for a response. On platform threads, a few hundred concurrent LLM calls exhaust your pool and requests queue. On virtual threads, each in-flight LLM call parks cheaply, and you handle far more concurrency on the same hardware — without rewriting your Spring AI service in a reactive style. For a blocking ChatClient.call() workload, virtual threads are close to a free scalability win.

    The pitfalls (read before you ship)

    • Pinning on synchronized. If a virtual thread blocks inside a synchronized block/method, it "pins" its carrier thread and can't be parked — killing the benefit. Replace hot-path synchronized with ReentrantLock. (Newer JDKs reduce this, but audit your blocking sections.)
    • Not a CPU-bound speedup. Virtual threads help I/O-bound concurrency. CPU-bound work is still limited by cores — they won't make computation faster.
    • Don't pool them. Virtual threads are cheap to create; pooling them defeats the purpose. Use Executors.newVirtualThreadPerTaskExecutor(), not a fixed pool.
    • Thread-locals. Heavy ThreadLocal usage across millions of threads can bloat memory; prefer scoped values or pass context explicitly.
    • Downstream limits still apply. Your database connection pool (e.g. HikariCP) is still finite — virtual threads let you accept more concurrent work, but a 10-connection pool is still a 10-connection bottleneck. Size downstream resources accordingly.

    When to use them

    • I/O-bound web apps — DB queries, HTTP/microservice calls, LLM calls. This is the sweet spot.
    • ✅ Apps you'd otherwise consider rewriting reactively just for throughput.
    • ⚠️ CPU-bound services — little benefit; focus on parallelism/algorithms instead.
    • ⚠️ Codebases with heavy synchronized on hot paths — fix pinning first.

    FAQ

    Do I need to rewrite my code? No — that's the whole point. Keep blocking, imperative code; flip the property. Just audit synchronized hot paths for pinning.

    Virtual threads or WebFlux? For most teams, virtual threads give you reactive-like scalability with simple blocking code and easier debugging. Choose WebFlux when you need true streaming/backpressure semantics end to end.

    Will this speed up a single request? No. It improves concurrency (how many requests you handle at once), not the latency of one request.

    What Spring Boot / Java version do I need? Spring Boot 3.2+ for the auto-configuration property, running on Java 21+ (the LTS that finalized virtual threads).


    Virtual threads are the rare "free lunch": keep your simple blocking Spring Boot code, add one property, and scale I/O-bound workloads — including slow LLM calls — dramatically further. Just mind the pinning and size your downstream pools.

    More: the Spring AI overview, streaming LLM responses in Spring AI, and AI system resilience.

    Scaling a Spring Boot AI service and want an architecture review? Let's talk.

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Was this helpful?
    AY
    Avaneesh Yadav

    I build enterprise AI systems — Spring AI, RAG, and agents — and write about shipping LLMs to production. I also run advisory and workshops for engineering teams.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Join engineers building production AI systems

    Comments