Unlock Massive Concurrency: How Java 21 Virtual Threads Supercharge Spring Boot

Java 21’s virtual threads provide lightweight, high‑concurrency execution managed by the JVM, enabling developers to create hundreds of thousands of threads with minimal overhead; this article explains their basics, demonstrates Spring Boot integration, compares performance against traditional threads, and shares additional Java optimization techniques.

Java High-Performance Architecture
Java High-Performance Architecture
Java High-Performance Architecture
Unlock Massive Concurrency: How Java 21 Virtual Threads Supercharge Spring Boot

Virtual threads, introduced in Java 21, simplify concurrent programming by being managed by the JVM rather than the operating system, offering lightweight memory usage, high concurrency, and automatic scheduling.

Lightweight : Virtual threads are managed by the JVM, so their memory footprint and creation cost are far lower than OS threads, allowing creation of hundreds of thousands of threads.

High Concurrency : They excel in I/O‑intensive scenarios, making it easy to build highly concurrent, responsive applications.

Automatic Management : No manual thread‑pool handling is required; the JVM adjusts scheduling based on load.

Basic Usage of Virtual Threads

Creating a virtual thread is straightforward; it can be started just like a regular thread but with far less overhead.

Thread virtualThread = Thread.ofVirtual().start(() -> {
    System.out.println("Virtual thread is running");
});
System.out.println("Main thread is running");

Virtual threads can also be created in an unstarted state and started later:

Thread virtualThread = Thread.ofVirtual()
    .name("VirtualThread")
    .unstarted(() -> System.out.println("Virtual thread running"));
virtualThread.start();
virtualThread.join(); // wait for completion

Using Virtual Threads in Spring Boot

Integrating virtual threads into a Spring Boot project requires a few simple steps.

Ensure the Java version is 21 or higher.

Enable preview features in pom.xml:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <configuration>
        <source>21</source>
        <target>21</target>
        <compilerArgs>
            <arg>--enable-preview</arg>
        </compilerArgs>
    </configuration>
</plugin>

Enable performance monitoring in application.properties:

management.endpoints.web.exposure.include=health,info,metrics

Configure Tomcat to use a virtual‑thread executor:

@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
    return protocolHandler -> protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
}
Spring Boot virtual thread configuration
Spring Boot virtual thread configuration

Experiment: Traditional Threads vs Virtual Threads

1. Create 100,000 Threads and Execute

Traditional Threads :

for (int i = 0; i < 100_000; i++) {
    Thread thread = new Thread(() -> System.out.println(i));
    thread.start();
    thread.join();
}

Execution time ≈ 18.6 seconds.

Virtual Threads :

for (int i = 0; i < 100_000; i++) {
    Thread thread = Thread.ofVirtual().unstarted(() -> System.out.println(i));
    thread.start();
    thread.join();
}

Execution time ≈ 3.7 seconds, a performance gain of nearly 500%.

2. HTTP Request Performance Comparison

In high‑concurrency scenarios, virtual threads show clear advantages. The test sends 1,600 HTTP requests with 400 concurrent workers.

@Bean
public TomcatProtocolHandlerCustomizer<?> protocolHandlerVirtualThreadExecutorCustomizer() {
    return protocolHandler -> protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
}

Traditional Threads : 9.659 s total, 165.65 requests/s.

Virtual Threads : 7.912 s total, 202.22 requests/s.

Virtual threads dramatically improve throughput and reduce response time.

Other Java Performance Optimization Techniques

Parallel Streams : Use parallelStream() for CPU‑bound tasks to leverage multiple cores.

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.parallelStream().forEach(n -> System.out.println(n * 2));

Asynchronous Programming with CompletableFuture : Reduce blocking for I/O‑bound work.

CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
    // async task
    System.out.println("Async task completed");
});
future.join();

Database Query Optimization : Minimize query count and use caching (e.g., Redis) to cut unnecessary I/O.

Memory Management : Employ object pools such as Apache Commons Pool to reuse resources and lower GC pressure.

Summary

Virtual threads revolutionize Java concurrency by simplifying thread management and boosting performance in high‑concurrency scenarios.

They allow creation of hundreds of thousands of threads without degrading application performance.

Integrating virtual threads into Spring Boot requires only a few configuration steps and yields significant speedups.

Additional techniques like parallel streams, CompletableFuture, query caching, and object pooling further enhance Java application performance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaPerformanceconcurrencySpring BootVirtual Threads
Java High-Performance Architecture
Written by

Java High-Performance Architecture

Sharing Java development articles and resources, including SSM architecture and the Spring ecosystem (Spring Boot, Spring Cloud, MyBatis, Dubbo, Docker), Zookeeper, Redis, architecture design, microservices, message queues, Git, etc.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.