Industry Ready Java Spring Boot, React & Gen AI — Live Course
JavaModern java

Virtual Threads

Introduction

Virtual threads are a major enhancement to Java’s concurrency model, introduced as a preview feature in Java 19 and finalized in Java 21 under Project Loom.

They allow developers to create millions of lightweight threads while writing simple, synchronous-style code. Virtual threads combine:

  • The simplicity of traditional blocking code
  • The scalability of asynchronous/reactive systems

This makes high-concurrency programming significantly easier and more accessible.


The Problem with Platform Threads

What Are Platform Threads?

Traditionally, every Java Thread maps 1:1 to an operating system (OS) thread. These are called platform threads.

Thread thread = new Thread(() -> {
    System.out.println("Running on platform thread");
});
thread.start();

Characteristics

  • Heavy (~1 MB stack per thread)
  • Managed and scheduled by the OS
  • Expensive to create and destroy
  • Limited scalability (thousands at most)

Scalability Issues

When handling large numbers of concurrent tasks (e.g., web requests):

  • Memory exhaustion (each thread consumes significant memory)
  • Excessive context switching
  • Thread creation overhead
  • Blocked threads waste system resources

Example problem pattern:

while (true) {
    Socket client = server.accept();
    new Thread(() -> handleRequest(client)).start();
}

Each request creates a new platform thread which is not scalable.


Traditional Workarounds (Before Virtual Threads)

1. Thread Pools

ExecutorService executor = Executors.newFixedThreadPool(100);
executor.submit(() -> handleRequest(client));

Limits thread count, but scalability is still capped.

2. Asynchronous Programming

CompletableFuture.supplyAsync(() -> readFromDatabase())
    .thenApply(data -> processData(data))
    .thenAccept(result -> sendResponse(result));

Better resource usage, but code becomes complex.

3. Reactive Programming

Highly scalable, but:

  • Complex programming model
  • Harder debugging
  • Steep learning curve

The Trade-off

Simple blocking code → Poor scalability Scalable async code → High complexity

Virtual threads remove this trade-off.


What Are Virtual Threads?

Virtual threads are lightweight, JVM-managed threads.

They:

  • Use very little memory (few KB)
  • Are scheduled by the JVM, not the OS
  • Can scale to millions
  • Use the same Thread API

Example:

Thread.startVirtualThread(() -> {
    System.out.println("Running on a virtual thread");
});

How Virtual Threads Work?

Carrier Threads

The JVM maintains a small pool of platform threads called carrier threads (usually equal to CPU cores).

Virtual threads run on top of these carrier threads.

Mounting and Unmounting

Virtual threads are mounted and unmounted from carrier threads:

  1. Virtual thread starts → mounted on carrier thread
  2. Blocking operation occurs (I/O, sleep) → unmounted
  3. Carrier thread becomes free
  4. When ready → virtual thread remounted

This enables massive scalability.

Key idea: The OS only sees carrier threads, not virtual threads.

Operations That Cause Unmounting

  • File I/O
  • Network I/O
  • Thread.sleep()
  • Object.wait()
  • BlockingQueue.take()
  • join()

Example:

Thread.startVirtualThread(() -> {
    readFromDatabase();   // unmount
    Thread.sleep(100);    // unmount
    callExternalApi();    // unmount
});

Creating Virtual Threads

1. Direct Creation

Thread.startVirtualThread(() -> {
    System.out.println("Hello");
});

2. Using Thread Builder

Thread.ofVirtual()
      .name("my-virtual-thread")
      .start(() -> doWork());
try (ExecutorService executor =
         Executors.newVirtualThreadPerTaskExecutor()) {

    executor.submit(() -> handleTask());
}

Each task gets its own virtual thread.

Virtual_Threads_Usage


Comparison Overview

FeaturePlatform ThreadsVirtual Threads
OS Mapping1:1Many-to-few
Memory UsageHigh (~1 MB)Low (few KB)
ScalabilityThousandsMillions
Code StyleBlockingBlocking
Best ForCPU-boundI/O-bound

Summary

  • Virtual threads are lightweight JVM-managed threads designed for high scalability.
  • They are not tied permanently to OS threads and can efficiently mount and unmount from carrier threads.
  • They allow applications to handle thousands or even millions of concurrent tasks using simple blocking code.
  • Virtual threads are best suited for I/O-bound workloads and do not improve CPU-bound performance.
  • Virtual threads simplify concurrent programming while enabling cloud-scale application design.

Written By: Muskan Garg

How is this guide?

Last updated on