Virtual Threads vs Platform Threads vs OS Threads: A Practical Guide!

With the advent of Project Loom, Java introduced Virtual Threads, a revolutionary new way to handle concurrency that stands in contrast to the traditional Platform Threads (also known as OS Threads). Understanding the differences between these types of threads is crucial for optimizing the performance and scalability of your applications, especially in highly concurrent environments.

In this practical guide, we’ll break down the characteristics, use cases, and pros and cons of Virtual Threads, Platform Threads, and OS Threads in Java, complete with examples to illustrate how and when to use each.


1. Understanding Platform Threads and OS Threads

Traditionally, Java threads are implemented as Platform Threads or OS Threads. These threads are tightly coupled to the operating system, each representing a single native thread that is scheduled and managed by the OS kernel.

Key Characteristics of Platform/OS Threads:

  • Heavyweight: Each thread consumes a significant amount of memory (about 1MB for the stack), and creating or switching threads involves considerable OS overhead.

  • Limited Scalability: OS threads are ideal for CPU-bound tasks, but they are limited in number due to resource constraints, which makes handling thousands of threads challenging.

  • Blocking Behavior: When an OS thread is blocked (e.g., on I/O operations), the entire thread remains idle, leading to wasted resources.

Example: Traditional Platform Thread Usage

public class PlatformThreadExample {

    public static void main(String[] args) {
        // Creating a Platform Thread
        Thread platformThread = new Thread(() -> {
            System.out.println("Executing task in Platform Thread");
            try {
                Thread.sleep(1000); // Simulating a blocking task
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Task completed");
        });

        platformThread.start();
    }
}

In this example, a traditional thread handles a task, but it blocks for 1 second. While suitable for small tasks, scaling this approach to thousands of concurrent threads would heavily impact performance.


2. Introducing Virtual Threads in Java

With Project Loom, Java introduced Virtual Threads, designed to be lightweight, highly scalable, and perfect for handling I/O-bound or high-concurrency tasks.

Key Characteristics of Virtual Threads:

  • Lightweight: Virtual threads are far lighter on resources, allowing Java to create millions of threads without overloading the system.

  • Efficient I/O Handling: Virtual threads handle I/O-bound tasks efficiently by not blocking OS threads, allowing them to pause and resume without burdening system resources.

  • Separation from OS Scheduling: Virtual threads are scheduled by the Java Virtual Machine (JVM) rather than the OS, allowing more flexible, efficient management.

Example: Virtual Threads with Project Loom

public class VirtualThreadExample {

    public static void main(String[] args) throws InterruptedException {
        // Creating a Virtual Thread to handle a task
        Thread virtualThread = Thread.ofVirtual().start(() -> {
            System.out.println("Executing task in Virtual Thread");
            try {
                Thread.sleep(1000); // Simulating a blocking task
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Task completed in Virtual Thread");
        });

        virtualThread.join(); // Wait for the virtual thread to complete
    }
}

In this example, the virtual thread handles a task that simulates a delay. Unlike traditional threads, creating thousands of such virtual threads would have minimal impact on memory usage and system resources.


3. Virtual Threads vs. Platform Threads

FeatureVirtual ThreadsPlatform Threads
Resource UsageMinimal (low memory overhead)High (larger memory footprint)
ScalabilitySupports millions of threadsLimited due to OS and memory limits
Best ForI/O-bound tasks, high concurrency workloadsCPU-bound tasks
Thread ManagementJVM-based, lightweight schedulingOS-based, heavyweight scheduling
Blocking BehaviorDoesn’t block OS threads on I/O waitsBlocks OS threads on I/O waits

4. When to Use Virtual Threads vs Platform Threads

When to Use Virtual Threads:

  • Handling Many Small Tasks: Virtual threads excel at handling tasks that are lightweight and involve frequent I/O operations, such as database queries or API calls.

  • Event-driven or Reactive Systems: In systems where tasks often pause due to waiting on external resources, virtual threads prevent OS threads from becoming bottlenecks.

  • High Concurrency Needs: Web servers or network applications that handle thousands of simultaneous requests can benefit from the scalability of virtual threads.

When to Use Platform Threads:

  • CPU-intensive Operations: Tasks that require significant computation, like data processing or scientific calculations, perform better on traditional OS threads.

  • Applications with Few Threads: For applications where only a small number of threads are needed, platform threads offer simplicity without the need for JVM-level scheduling.


5. Practical Examples: Using Virtual Threads and Platform Threads Together

Java applications can combine both virtual and platform threads to maximize efficiency, using each where they are best suited.

Example: Mixing Virtual and Platform Threads

public class MixedThreadExample {

    public static void main(String[] args) throws InterruptedException {
        // Platform thread for a CPU-bound task
        Thread platformThread = new Thread(() -> {
            System.out.println("Executing CPU-intensive task in Platform Thread");
            long sum = 0;
            for (long i = 0; i < 10_000_000; i++) {
                sum += i;
            }
            System.out.println("Sum calculated in Platform Thread: " + sum);
        });

        // Virtual thread for an I/O-bound task
        Thread virtualThread = Thread.ofVirtual().start(() -> {
            System.out.println("Executing I/O task in Virtual Thread");
            try {
                Thread.sleep(1000); // Simulate an I/O wait
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Completed I/O task in Virtual Thread");
        });

        platformThread.start();
        platformThread.join();
        virtualThread.join();
    }
}

Explanation: In this example, we use a platform thread for a CPU-intensive task (calculating a sum) and a virtual thread for an I/O-bound task (simulating a network call). This way, we allocate resources more efficiently based on the type of task.


6. Performance Comparison: Virtual Threads vs Platform Threads

To illustrate the performance benefits of virtual threads, let’s benchmark an application that spawns thousands of virtual and platform threads.

public class ThreadPerformanceComparison {

    public static void main(String[] args) throws InterruptedException {
        int threadCount = 10_000;

        // Benchmark for Platform Threads
        long startPlatform = System.currentTimeMillis();
        for (int i = 0; i < threadCount; i++) {
            Thread platformThread = new Thread(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
            platformThread.start();
        }
        long endPlatform = System.currentTimeMillis();
        System.out.println("Platform Threads took: " + (endPlatform - startPlatform) + " ms");

        // Benchmark for Virtual Threads
        long startVirtual = System.currentTimeMillis();
        for (int i = 0; i < threadCount; i++) {
            Thread virtualThread = Thread.ofVirtual().start(() -> {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        long endVirtual = System.currentTimeMillis();
        System.out.println("Virtual Threads took: " + (endVirtual - startVirtual) + " ms");
    }
}

Expected Results: This code creates 10,000 threads of each type and measures the time taken. Virtual threads should perform significantly better, highlighting their efficiency in handling high-concurrency workloads.


Conclusion

Understanding the distinctions between Virtual Threads, Platform Threads, and OS Threads enables you to choose the right approach for different concurrency needs:

  • Use Virtual Threads for applications with high concurrency and I/O-bound tasks.

  • Use Platform Threads for CPU-intensive tasks or when handling a limited number of threads is sufficient.

As Project Loom matures, virtual threads are likely to become the go-to solution for most concurrent programming needs in Java, providing lightweight concurrency without the limitations of traditional OS threads. This knowledge of virtual vs. platform threads will empower you to build highly efficient and scalable applications in Java.

More such articles:

https://medium.com/techwasti

https://www.youtube.com/@maheshwarligade

https://techwasti.com/series/spring-boot-tutorials

https://techwasti.com/series/go-language

0
Subscribe to my newsletter

Read articles from Maheshwar Ligade directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Maheshwar Ligade
Maheshwar Ligade

Learner, Love to make things simple, Full Stack Developer, StackOverflower, Passionate about using machine learning, deep learning and AI