All Articles
JavaConcurrencySoftware Engineering

Java Concurrency & Virtual Threads in Java 21+: Modern Multithreading Architecture

How Virtual Threads revolutionized high-throughput concurrent programming in modern Java

By Aditya Sharma 2026-08-08 12 min read• Peer Reviewed

For decades, Java threads (java.lang.Thread) were 1:1 wrappers around operating system (OS) kernel threads. Because kernel threads carry heavy memory footprints (~1MB stack) and high context-switching costs, servers were limited to handling thousands of concurrent connections before encountering thread exhaustion.

With Java 21 LTS, Project Loom introduced Virtual Threads — lightweight, user-mode threads managed by the JVM runtime rather than the OS kernel. This guide explains how Virtual Threads work and how to write high-throughput concurrent Java applications.

1. Platform Threads vs Virtual Threads

Traditional Platform Threads are mapped directly to OS kernel threads. When a platform thread performs a blocking I/O operation (database query, HTTP call, disk read), the underlying OS thread is paused and idle, wasting CPU resources.

Virtual Threads are managed entirely in JVM heap memory. When a virtual thread encounters blocking I/O, the JVM unmounts it from its carrier platform thread, allowing the carrier thread to execute other virtual threads. Once the I/O completes, the virtual thread is mounted back onto an available carrier thread seamlessly.

  • Memory footprint: ~1MB for platform threads vs ~few hundred bytes for virtual threads
  • Capacity: Millions of concurrent virtual threads on a single JVM instance
  • Preserves synchronous, easy-to-debug code style without complex reactive frameworks (Mono/Flux)

2. Creating and Using Virtual Threads

In Java 21+, launching virtual threads is straightforward with Executors.newVirtualThreadPerTaskExecutor() or Thread.ofVirtual().start(runnable).

Never pool virtual threads. Thread pools were invented to manage expensive platform threads. Because virtual threads are cheap and disposable, create a new virtual thread per task and let the garbage collector reclaim it when done.

3. Thread Pinning and ReentrantLock Best Practices

Thread Pinning occurs when a virtual thread cannot be unmounted from its carrier thread during blocking operations. This historically happened inside synchronized blocks or native JNI calls.

In modern Java development, replace synchronized blocks with java.util.concurrent.locks.ReentrantLock to prevent thread pinning and ensure full carrier thread utilization.

Frequently Asked Questions

Do Virtual Threads make CPU-intensive calculations faster?

No. Virtual Threads improve throughput for I/O-bound workloads (waiting on databases, APIs, networks). CPU-bound calculations (cryptography, video encoding, matrix math) are limited by the number of physical CPU cores, so platform threads or parallel streams are appropriate.

Should I replace existing Spring Boot thread pools with Virtual Threads?

In Spring Boot 3.2+, you can enable virtual threads with a single configuration property: spring.threads.virtual.enabled=true. This automatically configures Tomcat and task executors to use virtual threads for handling web requests.

AD

Written by Aditya Sharma

Technical contributor and subject matter specialist at PrimerPrep. Dedicated to breaking down complex systems into transparent, verified engineering principles.

Ready to test your knowledge?

Put these concepts into action with our independently reviewed practice questions and coding challenges.

Start practicing free