Skip to content Skip to footer

Virtual Threads in Java Project Loom

That is, a small number of platform threads is used to run many virtual threads. Whenever a virtual thread invokes a blocking operation, it should be “put aside” until whatever condition it’s waiting for is fulfilled, and another virtual thread can be run on the now-freed carrier thread. In terms of basic capabilities, fibers must run an arbitrary piece of Java code, concurrently with other threads (lightweight or heavyweight), and allow the user to await their termination, namely, join them. Obviously, there must be mechanisms for suspending and resuming fibers, similar to LockSupport’s park/unpark. We would also want to obtain a fiber’s stack trace for monitoring/debugging as well as its state (suspended/running) etc..

loom threads

Project Loom aims to drastically reduce the effort of writing, maintaining, and observing high-throughput concurrent applications that make the best use of available hardware. I use Thread.sleep when experimenting or demonstrating Java code for concurrency. By sleeping, I am faking some processing work taking place that will take some time.

Can you briefly describe what fibers or virtual threads actually are?

And that was very confusing and not at all the experience we wanted. And figuring out a way to define the relationship between the virtual thread and the OS thread that carries it, what we call the carrier thread, was something interesting that we only figured out a few months ago. So you can have pointers or references to objects on your stack. But the references to objects on the stack and references from other objects are handled completely differently by the GC. The stacks are known as GC roots and the GC starts with them and treats them specially. And the assumption there is that you won’t have too many stacks, but of course that assumption is broken with project Loom, because you might have a million stacks.

Virtual threads, on the other hand, are lightweight and can be created and managed entirely within the JVM. They are not bound to an operating system thread and do not require the same amount of system resources as traditional threads. This means that virtual threads can be created and destroyed more efficiently and can scale to support a higher number of concurrent requests. In other words, the carrier thread pool might be expanded when a blocking operation is encountered to compensate for the thread-pinning that occurs. A new carrier thread might be started, which will be able to run virtual threads. If you look closely, you’ll see invocations wrapped with a BufferedReader, which reads from the socket’s input.

Using Virtual Threads in Java

With virtual threads calling get won’t block the (OS) thread anymore. Without the penalty for using get you can use it whenever you like and don’t have to write asynchronous code. Reactive programming models address this limitation by releasing threads upon blocking operations such as file or network IO,
allowing other requests to be processed in the meantime. Once a blocking call has completed,
the request in question will be continued, using a thread again. This model makes much more efficient use of the threads resource for IO-bound workloads,
unfortunately at the price of a more involved programming model, which doesn’t feel familiar to many developers. Also aspects like debuggability or observability can be more challenging with reactive models,
as described in the Loom JEP.

loom threads

So we can see how the platform/kernel threads are actually on the core, blocked, as they wait for their 12-second Thread.sleep to expire. Then all five threads wake up at about the same moment, having all started at about the same moment, every 12 seconds, simultaneously do their math and write to console. This behavior is confirmed as we see little usage of the CPU cores in the Activity Monitor app. At any rate, the javadocs for Thread.sleep(…) in Loom currently do not mention any differences between kernel and virtual threads. Sleeping a virtual thread is handled like you would expect a virtual thread to behave.

Virtual Threads: JMeter meets Project Loom

Virtual threads are best suited to executing code that spends most of its time blocked, waiting for data to arrive on a network socket or waiting for an element in queue for example. It is possible to weave by manually threading the weft over and under the warp threads, but this is slow. Pin looms and peg looms also generally have no shedding devices.

loom threads

And the idea is that the block structure of your code mirrors the runtime behavior of the program. So just like structured programming, gives you that for sequential control flow, structured concurrency does the same for concurrency. So, you can see in the way that your code blocks are organized where a thread starts and where they end. And how does this help you with debuggers and profilers, because that expresses some logical relationship between various threads. You know that the child threads are doing some work on behalf of their parents and the parents are waiting for that work.

more stack exchange communities

So these problems are entirely a result of tasks not being mapped one-to-one to threads. The fact that we need to share our threads because they are so costly. But with virtual threads, they’re cheap enough to just have a single thread per task. And I will say that we won’t have many other problems as well, because once the thread captures the notion of a task, working with them becomes much simpler. So even though you’ll have more threads, I believe that will make working with threads much, much, much easier than having fewer threads.

If cloth needed to be wider, two people would do the task (often this would be an adult with a child). The weaver held a picking stick that was attached by cords to a device at both ends of the shed. With a flick of the wrist, one cord was pulled and the shuttle was propelled through the shed to the other end with considerable force, speed and efficiency. A flick in the opposite direction and the shuttle was propelled back. A single weaver had control of this motion but the flying shuttle could weave much wider fabric than an arm’s length at much greater speeds than had been achieved with the hand thrown shuttle. In a drawloom, a “figure harness” is used to control each warp thread separately,[24] allowing very complex patterns.

High-throughput / Lightweight

A caveat to this is that applications often need to make multiple calls to different external services. Project Loom’s mission is to make it easier to write, debug, profile and maintain concurrent applications meeting today’s requirements. Project Loom will introduce fibers as lightweight, efficient threads managed by the Java loom threads Virtual Machine, that let developers use the same simple abstraction but with better performance and lower footprint. A fiber is made of two components — a continuation and a scheduler. As Java already has an excellent scheduler in the form of ForkJoinPool, fibers will be implemented by adding continuations to the JVM.

  • An alternative solution to that of fibers to concurrency’s simplicity vs. performance issue is known as async/await, and has been adopted by C# and Node.js, and will likely be adopted by standard JavaScript.
  • There are two secondary motions, because with each weaving operation the newly constructed fabric must be wound on a cloth beam.
  • Java has had good multi-threading and concurrency capabilities from early on in its evolution and can effectively utilize multi-threaded and multi-core CPUs.
  • However, if a failure occurs in one subtask, things get messy.
  • In the literature, nested continuations that allow such behavior are sometimes call “delimited continuations with multiple named prompts”, but we’ll call them scoped continuations.
  • Since Java 5.0, We’ve encouraged people not to use the thread API directly for most things.

However, the CPU would be far from being utilized since it would spend most of its time waiting for responses from the external services, even if several threads are served per CPU core. There are two specific scenarios in which a virtual thread can block the platform thread (called pinning of OS threads). Notice the blazing fast performance of virtual threads that brought down the execution time from 100 seconds to 1.5 seconds with no change in the Runnable code.

thought on “Project Loom: Threads Vs Virtual Threads2 min read”

All these threads will be closed in parallel when we exit the scope. If the DB thread is closed first, the other threads
have nowhere to write to before they are also closed. Until the common backend frameworks (in the example above, we used Spring) support virtual threads, we will have to be patient for a while. In addition, the database drivers and drivers for other external services must also support the asynchronous, non-blocking model. Anyone who has ever maintained a backend application under heavy load knows that threads are often the bottleneck. For every incoming request, a thread is needed to process the request.

Leave a comment


Go to Top