How I Spun Up 5 Million Virtual Threads Without Stalling The JVM.

Explore how I unleashed 5 million Virtual Threads seamlessly on my M1 Macbook Pro using Project Loom, pushing the boundaries of Java concurrency without JVM stalling.

How I Spun Up 5 Million Virtual Threads Without Stalling The JVM.
Photo by ALAN DE LA CRUZ / Unsplash

This article was originally published in Medium when project Loom was in preview. Now that Loom is available for us all to cherish, presenting an updated version of the blog here with stuff that are no longer relevant trimmed out for you to enjoy.

This article exemplifies how I spun up 5 million Java Virtual threads that each sleep for 100 milliseconds without stalling the JVM on my M1 Macbook Pro. Project Loom makes this possible.

Here, I spun up 1 million Virtual Threads (will upgrade to 5 million later) with the help of the new Thread.startVirtualThread() function.

private static void startVirtualThreads_1_Million() throws InterruptedException {
    var threads = new ArrayList<Thread>(1000000);
    for (int i = 0; i < 1000000; i++) {
        var thread = startVirtualThread(() -> {
            System.out.println(Thread.currentThread());
            try {
                sleep(100);
            } catch (InterruptedException e) {
                System.out.println("Exception Occurred: " + e.getMessage());
            }
        });
        threads.add(thread);
    }
    for (Thread thread : threads) {
        thread.join();
    }
}
GIF captured when the code above was run using IntelliJ Idea

This starts and waits for all the 1 Million virtual threads created to complete. All the Virtual Threads would have completed execution before “Main Ended” is printed on the screen.

Virtual Threads don’t block when sleep() or most other blocking operations are invoked (for example, Future.get()). Instead, they unmount themselves from the platform thread they’re running on when they’re about to be blocked, allowing other virtual threads to mount themselves onto the platform thread and run. The Virtual threads return to the JVM’s scheduler when they’re ready to resume execution (in this case, when the sleep time is exhausted).

Virtual Threads, by default, are daemon threads, meaning the JVM would NOT wait for these threads to complete. Hence, I collected the threads and joined them to make the JVM wait for the completion of those million threads.

I removed the rest of the print statements as they were distracting. I also up’ed the number of threads to 5 Million. Here's the updated code.

private static void startVirtualThreads_1_Million() throws InterruptedException {
    var threads = new ArrayList<Thread>(5000000);
    for (int i = 0; i < 1000000; i++) {
        var thread = startVirtualThread(() -> {
            try {
                sleep(100);
            } catch (InterruptedException e) {
                System.out.println("Exception Occurred: " + e.getMessage());
            }
        });
        threads.add(thread);
    }
    for (Thread thread : threads) {
        thread.join();
    }
}

The response was:

Main Started
Main Ended
Process finished with exit code 0

Thus, I was able to spin up 5 Million Virtual Threads on my M1 machine.

Explore the code on GitHub, and consider following me on GitHub too for updates on exciting projects I embark upon. 😉