Core Java

How to Use Java 21’s Virtual Threads in Real-World Web Applications

With the release of Java 21, virtual threads — part of Project Loom — are now stable and production-ready. This revolutionary feature enables developers to write high-throughput, scalable concurrent applications with a simplified programming model. For Spring Boot developers, especially those using version 3.2 or newer, virtual threads unlock a new era of performance for I/O-bound web applications.

In this article, you’ll learn:

  • What virtual threads are and how they differ from traditional threads.
  • How to integrate them into Spring Boot 3.2+ applications.
  • Real-world examples.
  • Best practices and caveats.
  • Helpful references and resources.

☁️ What Are Virtual Threads?

Virtual threads are lightweight threads that are managed by the JVM, not the operating system. They’re designed to dramatically reduce the cost of concurrent programming.

🔍 Key Benefits:

  • Near-zero overhead in thread creation.
  • Enables writing blocking code with the scalability of asynchronous models.
  • Excellent for I/O-bound applications like web servers.

🧵 Traditional threads: ~2MB of stack memory
🪶 Virtual threads: ~few KB and managed by JVM, not OS kernel

📖 Java Virtual Threads Documentation

⚙️ Enabling Virtual Threads in Spring Boot 3.2+

Spring Boot 3.2+ introduces first-class support for virtual threads via configuration and the updated TaskExecutor API.

✅ Prerequisites

  • Java 21+
  • Spring Boot 3.2 or higher
  • Spring Web or WebFlux

🛠️ Example: Virtual Threads in Spring MVC Controller

@RestController
@RequestMapping("/api")
public class VirtualThreadController {

    @GetMapping("/process")
    public String processRequest() {
        try {
            Thread.sleep(2000); // Simulate I/O-bound operation
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return "Processed by thread: " + Thread.currentThread();
    }
}

With virtual threads, this blocking Thread.sleep() won’t harm scalability.

🧰 Step-by-Step Configuration

1. Use Virtual Thread Executor

@Configuration
public class VirtualThreadConfig {

    @Bean
    public Executor taskExecutor() {
        return Executors.newVirtualThreadPerTaskExecutor();
    }
}

This sets up an Executor that creates a new virtual thread for every task.

ℹ️ This configuration is used by Spring’s @Async, RestTemplate, and other task-based components.

2. Enable Asynchronous Methods

@EnableAsync
@SpringBootApplication
public class VirtualThreadApp {
    public static void main(String[] args) {
        SpringApplication.run(VirtualThreadApp.class, args);
    }
}

Now, any @Async method will use virtual threads:

@Async
public CompletableFuture<String> heavyComputation() {
    Thread.sleep(3000);
    return CompletableFuture.completedFuture("Done in " + Thread.currentThread());
}

🌐 Real-World Scenario: Handling Thousands of Concurrent HTTP Requests

Imagine a REST API that queries a remote service for stock data. Normally, this would require reactive code to achieve scalability. But with virtual threads, you can stay imperative:

@GetMapping("/stocks")
public ResponseEntity<String> getStockInfo() throws IOException {
    URL url = new URL("https://api.example.com/stock/ABC");
    try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()))) {
        String response = in.readLine();
        return ResponseEntity.ok(response);
    }
}

This blocking I/O operation is efficient and scalable with virtual threads — no need to rewrite using WebClient or reactive paradigms.

🧪 Benchmark: Loom vs. Traditional Threads

A simple comparison on a Spring Boot server with:

  • 10,000 concurrent requests
  • Each request waits for 2s (simulating DB call)
Thread ModelAverage LatencyCPU UsageMemory Footprint
Platform ThreadsHighHigh~20GB (OOM likely)
Virtual ThreadsLowModerate~2GB

📚 Official Performance Benchmarks (JEP 444)

✅ Best Practices

  1. Avoid CPU-bound work in virtual threads: They’re ideal for I/O-heavy workloads.
  2. Use structured concurrency (Java 21 preview) to manage thread lifecycles.
  3. Profile your application to identify blocking points (e.g., JDBC calls).
  4. Use Thread.ofVirtual().start() for ad-hoc concurrency outside of Spring:
Thread.startVirtualThread(() -> {
    // some blocking task
});

⚠️ Gotchas

  • Not all libraries are virtual-thread friendly. Watch out for native synchronization primitives (synchronized, wait()).
  • Connection pool exhaustion: You still need to tune database pools (or use R2DBC).
  • Monitoring: Many observability tools don’t yet fully support virtual threads.

📖 See Spring Docs on Virtual Threads

🧩 Combining Virtual Threads with Structured Concurrency

Structured concurrency (preview feature in Java 21) allows managing task hierarchies cleanly:

try (var scope = StructuredTaskScope.ShutdownOnFailure.open()) {
    Future<String> task1 = scope.fork(() -> fetchData());
    Future<String> task2 = scope.fork(() -> computeSomething());
    scope.join();
    scope.throwIfFailed();
    return task1.result() + task2.result();
}

📘 JEP 453 – Structured Concurrency (Preview)

🔚 Conclusion

Virtual threads in Java 21 bring back the simplicity of synchronous code with the scalability of asynchronous models. For Spring Boot 3.2+ developers, this means writing readable, efficient web apps capable of handling massive concurrency without the complexity of reactive frameworks.

Now you can:

  • Embrace blocking I/O with confidence.
  • Simplify your codebase.
  • Improve scalability without rewriting everything in a reactive style.

🔗 Further Reading

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button