• Skip to main content
  • Skip to secondary menu
  • Skip to primary sidebar
  • Skip to footer
  • Home
  • Contact Us

All about code

  • Java
    • Spring
      • Spring Boot
      • Spring Cloud
    • Java Miscellaneous Tips
    • Java Design Patterns
  • Data Structures and Algorithms
    • Algorithms
    • Data Structures
  • Thoughts and Stories
  • AI

Brief and Easy Explanation of Virtual Threads

coffee cup and saucer beverage tea 24961

Introduction

Virtual threads are lightweight, JVM-managed threads introduced in Java 21 through Project Loom. They allow you to create millions of threads on commodity hardware without exhausting system resources, because the operating system remains unaware of their existence. Each virtual thread is mounted on a small pool of carrier threads that are ordinary Java platform threads. When a virtual thread blocks on I/O, the JVM detaches it from the carrier so another virtual thread can run, giving you near-optimal hardware utilization without writing reactive or callback-based code.

Traditional Platform Thread Example

The classic approach uses Thread or an ExecutorService backed by platform threads. The snippet below launches 10000 tasks that each sleep for one second. On most laptops the program either crashes with an out-of-memory error or takes a long time to start, because each platform thread consumes roughly one megabyte of stack space.

public class PlatformThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        try (var executor = Executors.newFixedThreadPool(10_000)) {
            for (int i = 0; i < 10_000; i++) {
                final int id = i;
                executor.submit(() -> {
                    try {
                        Thread.sleep(Duration.ofSeconds(1));
                    } catch (InterruptedException ignored) {
                    }
                    return id;
                });
            }
        }
    }
}

Virtual Thread Example

With virtual threads the same workload runs comfortably in a few megabytes of heap. The only change is the factory method used to create the executor.

public class VirtualThreadDemo {
    public static void main(String[] args) throws InterruptedException {
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 10_000; i++) {
                final int id = i;
                executor.submit(() -> {
                    try {
                        Thread.sleep(Duration.ofSeconds(1));
                    } catch (InterruptedException ignored) {
                    }
                    return id;
                });
            }
        }
    }
}

Running this version typically finishes in a little over one second on an eight-core machine, demonstrating that virtual threads scale to task count rather than core count.

Structured Concurrency with Virtual Threads

Virtual threads shine when paired with structured concurrency, introduced in Java 21 as an incubating API. The following example fetches a user profile and order history concurrently while guaranteeing that both subtasks complete or fail together.

public class StructuredConcurrencyDemo {
    record User(String name) {}
    record Order(long id) {}

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            Supplier<User> user = scope.fork(() -> fetchUser());
            Supplier<Order> order = scope.fork(() -> fetchOrder());

            scope.join().throwIfFailed();

            System.out.println(STR."User: \{user.get()}, Order: \{order.get()}");
        }
    }

    private static User fetchUser() throws InterruptedException {
        Thread.sleep(Duration.ofMillis(200));
        return new User("Alice");
    }

    private static Order fetchOrder() throws InterruptedException {
        Thread.sleep(Duration.ofMillis(300));
        return new Order(42);
    }
}

The StructuredTaskScope automatically cancels remaining subtasks if any fail, eliminating the common leaks and race conditions found in raw Future code.

When Not to Use Virtual Threads

Virtual threads are not a silver bullet. They are ideal for workloads that spend most of their time blocked on I/O, but they provide no advantage for CPU-bound computations because only one virtual thread runs on a carrier thread at any instant. If your task saturates the CPU, stick to the existing fork-join pool or reactive streams. Additionally, avoid pooling virtual threads; doing so negates their scalability benefits and creates contention on the pool’s internal locks.

Conclusion

Virtual threads remove the historical trade-off between simple, synchronous code and high scalability. By letting the JVM manage millions of lightweight threads, you can write straightforward blocking logic and still utilize hardware efficiently. Adopt virtual threads for I/O-heavy workloads, combine them with structured concurrency to prevent leaks, and continue using platform threads or specialized frameworks when computation dominates. The result is clearer, safer code that scales from laptops to cloud deployments without rewriting your mental model of Java concurrency.

Primary Sidebar

Social

  • Facebook
  • LinkedIn
  • Twitter

Archives

  • May 2026
  • April 2026
  • March 2026
  • August 2025
  • July 2025
  • June 2025
  • May 2025
  • April 2025
  • March 2025
  • December 2024
  • October 2024
  • August 2024
  • July 2024
  • March 2024
  • February 2024
  • January 2024
  • December 2023
  • September 2023
  • May 2023
  • March 2023
  • January 2023
  • November 2022
  • September 2022
  • August 2022

Recent Posts

  • How AI Coding Assistants Are Reshaping Software Engineering Careers
  • Better Avoid Spring Boot Lazy-initialization @PostConstruct Trap
  • How To Handle Large Datasets in Spring Boot
  • How to Log HTTP Incoming Requests in Spring Boot
  • How to Reliably Implement Post-Commit Actions in Spring

TAGS

AI Algorithms Apache POI Backtracking Date Structures Dynamic Programming engineering Graphs Greedy Horror Stories HSSF Java Java New Features Java Principles Java tips real life Sorting Spring Boot Spring Cloud Strings Trees

Footer

Privacy Policy Cookie Policy Terms and Conditions