Java 21 Feature

Virtual Threads

Lightweight threads that dramatically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications.

1M+Concurrent Threads
~1KBMemory per Thread
1000xLess Memory

What are Virtual Threads?

Virtual threads are lightweight threads introduced in Java 21 (JEP 444) that enable high-throughput concurrent applications. They are managed by the JVM rather than the operating system, allowing you to create millions of threads efficiently.

Massive Scalability

Create millions of virtual threads instead of being limited to thousands of platform threads. Perfect for I/O-bound workloads.

Low Memory Footprint

Each virtual thread uses only ~1KB of memory compared to ~1MB for platform threads. Run more concurrent tasks with less RAM.

Simple Programming Model

Write straightforward blocking code without complex async patterns. The JVM handles the complexity of efficient scheduling.

Seamless Integration

Virtual threads work with existing Java code. Use familiar APIs like ExecutorService, locks, and blocking I/O operations.

Fast Context Switching

Context switching happens in user space, not kernel space. Much faster than OS-level thread switching.

Automatic Carrier Management

Virtual threads automatically mount and unmount from carrier threads. When blocked on I/O, the carrier is freed for other work.

When to Use Virtual Threads

Ideal Use Cases

  • Web servers handling many concurrent requests
  • Database-heavy applications with connection pools
  • Microservices making multiple API calls
  • File processing and batch operations

Not Recommended For

  • CPU-intensive computations (use platform threads)
  • Long-running synchronized blocks
  • Native code that blocks in JNI
  • Code using ThreadLocal for pooled resources

Evolution of Java Concurrency

Java 1.0 (1996)
Basic Thread class introduced
Java 5 (2004)
java.util.concurrent package, ExecutorService
Java 7 (2011)
Fork/Join framework for parallel computation
Java 8 (2014)
CompletableFuture, parallel streams
Java 19 (2022)
Virtual Threads (Preview)
Java 21 (2023)
Virtual Threads (GA) - JEP 444

Thread Architecture Comparison

Platform Threads (1:1)

Java Threads
T0
T1
T2
T3
1:1
1:1
1:1
1:1
OS Threads
OS0
OS1
OS2
OS3
T0 running...

Virtual Threads (M:N)

Virtual Threads
V0
V1
V2
V3
V4
V5
+ millions more...
mounted on
Carrier Threads
C02 VTs
C11 VTs
OS Threads
OS0
OS1
VTs swap in/out dynamically
  • • Each Java thread = one OS thread
  • • ~1MB memory per thread
  • • Blocked thread = wasted OS thread
  • • Limited to ~4,000 threads
  • • Expensive context switching
  • • Many virtual threads share few carriers
  • • ~1KB memory per thread
  • • Blocked = unmount, carrier freed
  • • Millions of threads possible
  • • Fast user-space scheduling

Platform vs Virtual Threads Comparison

Metric
Platform Threads
Virtual Threads
Memory per Thread
1MB
0.001MB
Max Concurrent Threads
4,000
1,000,000
Thread Creation Time
1,000μs
1μs
Context Switch Cost
High
Low
OS Thread Mapping
1:1
M:N
Blocking I/O Impact
Thread Blocked
Carrier Released

Memory Usage Visualization

Comparison for running 10,000 concurrent threads

Platform Threads~10 GB RAM
Virtual Threads~10 MB RAM

Virtual threads use ~1000x less memory than platform threads

Java 21 Virtual Threads Code Examples

Basic Virtual Thread

Create a simple virtual thread using Thread.startVirtualThread()

Basic Virtual Thread.java
1// Java 21 - Virtual Thread (Basic)
2Thread.startVirtualThread(() -> {
3 System.out.println("Running in virtual thread!");
4 // Perform I/O operation
5 String result = fetchDataFromAPI();
6 System.out.println("Result: " + result);
7});

Virtual Thread Executor

Use ExecutorService with virtual threads for concurrent tasks

Virtual Thread Executor.java
1// Java 21 - Virtual Thread Executor
2try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
3 // Submit 10,000 concurrent tasks
4 IntStream.range(0, 10_000).forEach(i -> {
5 executor.submit(() -> {
6 Thread.sleep(Duration.ofSeconds(1));
7 return fetchData(i);
8 });
9 });
10} // Executor auto-closes and waits for all tasks

Platform Thread (Old Way)

Traditional platform thread approach - limited scalability

Platform Thread (Old Way).java
1// Traditional Platform Threads (Pre-Java 21)
2// Limited to ~4,000 threads due to OS constraints
3ExecutorService executor = Executors.newFixedThreadPool(200);
4
5for (int i = 0; i < 10_000; i++) {
6 executor.submit(() -> {
7 // Each task blocks a precious OS thread
8 Thread.sleep(Duration.ofSeconds(1));
9 return fetchData();
10 });
11}
12// Many tasks waiting in queue, poor throughput

Structured Concurrency

Use StructuredTaskScope for better task management (Preview)

Structured Concurrency.java
1// Java 21 - Structured Concurrency (Preview)
2try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
3 // Fork subtasks as virtual threads
4 Subtask<User> userTask = scope.fork(() -> fetchUser(id));
5 Subtask<Order> orderTask = scope.fork(() -> fetchOrder(id));
6
7 scope.join(); // Wait for all
8 scope.throwIfFailed(); // Handle errors
9
10 // Both completed successfully
11 return new Response(userTask.get(), orderTask.get());
12}

HTTP Server Example

High-throughput HTTP server handling millions of connections

HTTP Server Example.java
1// Java 21 - HTTP Server with Virtual Threads
2var server = HttpServer.create(new InetSocketAddress(8080), 0);
3server.setExecutor(Executors.newVirtualThreadPerTaskExecutor());
4
5server.createContext("/api", exchange -> {
6 // Each request runs in its own virtual thread
7 // Can handle 1M+ concurrent connections!
8 String data = queryDatabase(); // Blocking I/O is fine
9 byte[] response = data.getBytes();
10 exchange.sendResponseHeaders(200, response.length);
11 exchange.getResponseBody().write(response);
12});
13
14server.start();
!

Key Insight

Virtual threads let you write simple, blocking code while achieving the performance of complex async/reactive patterns. No more callback hell or complex reactive streams for I/O-bound applications!

Interactive Thread Simulation

50

Platform Threads

0/50 completed
Running
Blocked (I/O)
Waiting
Completed

Virtual Threads

0/50 completed
Running
Blocked (I/O)
Waiting
Completed