javamultithreadingvirtual-threads

Do Java 21 virtual threads address the main reason to switch to reactive single-thread frameworks?


To my understanding one of the reasons to switch to a reactive (e.g. Project Reactor, RxJava, Vert-X) or actor (Akka) framework is because of the cost of thread switching is high. Looking at Difference between platform thread, carrier thread and virtual thread in context of Java 21 and later and other information about virtual threads I was wondering...

Do Virtual Threads remove the reason to switch to a different paradigm because it will just swap out the blocking virtual thread to a different thread on the carrier?


Solution

  • Yes, virtual threads eliminate the need for reactive approach

    Platform threads in Java are mapped directly to a thread host operating system thread. Those OS threads are “expensive” in terms of memory and CPU.

    Virtual threads, in contrast, are managed within the JVM. As a result, virtual threads are extremely “cheap”, meaning they are quite efficient in both memory and CPU. With virtual threads, you can reasonable expect to run even millions of tasks simultaneously on common computer hardware.

    Yes, most, if not all, of the work done as reactive code can be done instead with Java virtual threads. The coding is vastly simpler to write, comprehend, trace, and debug. Reactive approach was invented to get around the performance problems of over-using platform threads.

    See video of Brian Goetz being asked what he sees as the future of reactive programming after the arrival of Project Loom and virtual threads:

    I think Loom is going to kill reactive programming … reactive programming was a transitional technology …

    Making reactive programming unnecessary was one of the major motivations for inventing virtual threads in Java.

    Virtual threads are for blocking code

    Caveat… Virtual threads are contra-indicated for tasks that are CPU-bound such as video encoding/decoding. Use virtual threads only for code that involves blocking, such logging, file I/O, accessing databases, network calls. That would cover nearly all Java business apps.

    Cheap threads can perform expensive tasks

    Of course, the tasks performed by these many cheap virtual threads may involve significant resources. Those resources might include consumption of large amounts of memory, tying up a limited number of network ports, overloading a database with too many connections, and so on.

    In the old days, Java programmers learned that the small number of platform threads that were practical for multi-threading indirectly acted as a throttle on excessive use of resources by the executing tasks. Now, with potentially millions of virtual threads, you may need to add explicit throttling of expensive tasks to conserve precious resources. You can use throttling mechanisms such as Semaphore permits or ReentrantLock.

    For more discussion, see Answer by Teddy.

    Pinning

    A weakness in the current implementation of virtual threads is that in some cases the virtual thread is “pinned” to its carrier platform thread, meaning it cannot be set aside while blocked for that carrier platform thread to be assigned another virtual thread. Pinning defeats the performance of virtual threads.

    As of Java 22, pinning happens in at least two scenarios:

    To be clear: You can use synchronized and native code in your virtual threads. But if the task being executed includes significantly long periods of such work, then assign that task to a platform thread rather than a virtual threads. Or in the case of long code protected by synchronized (long-running code, not all code), replace with a ReentrantLock. Excessive pinning impairs the efficiency and effectiveness of other virtual thread usages.

    The Project Loom team continues their work. They are looking into ways to decrease situations resulting in pinning. So the situation will likely change in future versions of Java. See 2024-10 update in video by Alan Bateman. Pinning due to synchronized may be eliminated in Java 24 per JEP 491.

    You can easily detect protracted pinning. Java will emit a new JDK Flight Recorder (JFR) event, jdk.VirtualThreadPinned, every time a Virtual Thread gets pinned, with a threshold of 20ms by default.

    For more info

    For details, see firstly the official document, JEP 444: Virtual Threads. See also the official Project Loom site.

    Then see the more recent videos of presentations by Ron Pressler, Alan Bateman, or José Paumard. Publicly available on YouTube, etc.

    See an entire presentation focused on this topic: Are Virtual Threads Going to Make Reactive Programming Irrelevant?, 2024-10, by José Paumard.

    If you really want to understand how the impressive performance gains were made, see the talk on Continuations by Ron Pressler: Continuations - Under the Covers. To be clear: This understanding is entirely optional, unnecessary to making effective use of virtual threads. But if you need to satisfy your geek curiosity, you’ll enjoy that particular talk by Pressler.