Java 19 brings the first preview of virtual threads to the Java platform; this is the main deliverable of OpenJDKs Project Loom. This is one of the biggest changes to come to Java in a long time — and at the same time, is an almost imperceptible change. There is almost zero new API surface, and virtual threads behave almost exactly like the threads we already know.
So, we don’t need to allocate a gazillion of memory to fit every possible use case. In such an approach, every thread can use its own local variable to store information. The need to share mutable states among threads, the well-known “hard part” of concurrent programming, drastically decreases. However, using such an approach, we can easily reach the limit of the number of threads we can create. As we may know, the JVM gives us an abstraction of OS threads through the type java.lang.Thread. Until Project Loom, every thread in the JVM is just a little wrapper around an OS thread.
Not the answer you’re looking for? Browse other questions tagged javamultithreadingproject-loom or ask your own question.
The above code also shows how the jdk.tracePinnedThreads flag works. The VTHREAD_SCOPE is a ContinuationScope object, a class used to group continuations. In other words, it’s a way to group continuations related to each other. In our case, we have only one ContinuationScope object, the VTHREAD_SCOPE object.
Here is a diagram showing mariadb developers – executed by platform threads – which are again executed by OS threads. This is explained in more detail in later sections of this
Java virtual thread tutorial. Our team has been experimenting with Virtual Threads since they were called Fibers. Since then and still with the release of Java 19, a limitation was prevalent, leading to Platform Thread pinning, effectively reducing concurrency when using synchronized. The use of synchronized code blocks is not in of itself a problem; only when those blocks contain blocking code, generally speaking I/O operations. In fact, the same blocking code in synchronized blocks can lead to performance issues even without Virtual Threads.
Runnable vs. Callable
This way, the carrier thread can execute any other eligible virtual threads. Once the blocked virtual thread finishes the blocking operation, the scheduler schedules it again for execution. The execution can continue on the same carrier thread or a different one. As we said, virtual threads are a new type of thread that tries to overcome the resource limitation problem of platform threads. They are an alternate implementation of the java.lang.Thread type, which stores the stack frames in the heap (garbage-collected memory) instead of the stack. For application programmers, they represent an alternative to asynchronous-style coding such as using callbacks or futures.
The most basic way to use a virtual thread is with Thread.startVirtualThread(Runnable r). This is a replacement for instantiating a thread and calling thread.start(). Because virtual threads are threads and have little new API surface of their own, there is relatively little to learn in order to use virtual threads. But there are actually quite a few things we need to unlearn in order to use them effectively. Pinning is harmless if synchronized is used to avoid a race condition in an in-memory operation.
Java Virtual Threads
Virtual threads require few resources and a single Java virtual machine may support millions of virtual threads. Virtual threads are a great choice for executing tasks that spend much of their time blocked, often waiting for I/O operations to complete. If one squints, the above behavior is not all that different from current scalable code that makes use of NIO channels and selectors – which can be found in many server-side frameworks and libraries.
Ultimately, we wouldn’t need to reach out for NIO or Async APIs. This should result in more readable code that is easier to understand and debug. Nevertheless, the continuation can potentially block a carrier thread — specifically, when a thread calls a native method and performs blocking operations from there. The retrieveURLs method creates a list of tasks (one for each URL) and submits them to the executor, then waits for the results.
Maintaining Context and Debugging
If you notice, in the above program in line #4, we are invoking ‘Thread.startVirtualThread()’ method by passing a Runnable object as an argument, which is created in line #2. A function can return a promise, for example, the result of an HTTP request, and then caller functions can chain their logic to it. Java also has promised, but they are called Futures, however, only the CompletableFuture has the complete feature list of a Promise. Most operations in Java are blocking and Futures occupy the thread anyways.
- Newer versions of the builds are free to change and break current APIs.
- So we may get scalability from this model, but we have to give up on using parts of the language and ecosystem to get it.
- You can even use the JFR API to define custom metrics for your threads.
- Instead, we preferred to use a thread pool or an executor service configured with a thread pool.
- As we can see, regardless of the underlying implementation, the API is the same, and that implies we could easily run existing code on the virtual threads.
It is a continuation and a scheduler that, together, make up a virtual thread. Now, our user-mode scheduler may be any implementation of the Executor interface. The above example has shown us that, by default, we run on the ForkJoinPool.
Some Virtual Threads Internals
In case I didn’t explain something well feel free to comment and challenge me. I will also link all of the resources and further readings below so you can do your research if needed. Loom is still in the pre-release version, things might change, but one thing is certain, we will be getting Virtual Threads in JDK 19, the official release date, by the time I am writing this, is September 2022.
Unfortunately, Java 19 is not an LTS and if you are working for one of those companies that only use LTS versions you will have to wait for Java 21 which should be released in September 2023. I have marked in red threads that need to be stopped immediately after a certain state is reached. With Loom we can do that, currently other than the special thread pool they will introduce we have no other way other than manually stopping the threads, but this JEP promises to bring more utilities to manage this. If are you using Postgres with a connection pool of 50, then spawning more than 50 threads (platform or non-platform) won’t make any difference. Routines (aka. Subroutines) are reusable pieces of code that are usually called multiple times during execution. Imagine them as an immutable set of instructions with input and output that you can call whenever you like.
Statistical Concepts Every Data Scientist/Analyst Should Know
Java’s concurrency utils (e.g. ReentrantLock, CountDownLatch, CompletableFuture) can be used on Virtual Threads without blocking underlying Platform Threads. This change makes Future’s .get() and .get(Long, TimeUnit) good citizens on Virtual Threads and removes the need for callback-driven usage of Futures. Before digging into virtual threads, let us first understand how the threads work in traditional threads in Java. The workingHard virtual thread is never unmounted from the carrier thread, and the takeABreak virtual thread is never scheduled. Therefore, the initial memory footprint of a virtual thread tends to be very small, a few hundred bytes instead of megabytes.
Virtual threads improve application throughput since you can have many more concurrent tasks than with platform threads. For example, a web service may not tolerate huge numbers of concurrent requests. The synchronous Java networking APIs have been re-implemented by JEP 353 and JEP 373 in preparation for Project Loom. When run in a virtual thread, I/O operations that do not complete immediately will result in the virtual thread being parked.
By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct. I strongly suspect that Java threads have a performance advantage on Windows, but not on Linux. The code above breaks at around 25k with an OOM exception when I use Platform threads. Spring Runtime offers support and binaries for OpenJDK™, Spring, and Apache Tomcat® in one simple subscription. We very much look forward to our collective experience and feedback from applications.