Java Virtual Threads

Jakob Jenkov
Last update: 2023-02-04

Java virtual threads are a new thread construct added to Java from Java 19. Java virtual threads are different from the original platform threads in that virtual threads are much more lightweight in terms of how many resources (RAM) they demand from the system to run. Thus, you can have far more virtual threads running in your applications than platform threads.

With more virtual threads running you can do more blocking IO in parallel than with fewer platform threads. This is useful if your application needs to make many parallel network calls to external services such as REST APIs, or open many connections to external databases (via JDBC) or similar.

Virtual Threads are Still a Preview Feature

Java virtual threads are still a preview feature in Java 19, meaning their implementation and the API to create them may still change. Also, you need to switch on Java preview features in your IDE to use them!

Java Virtual Threads Tutorial Video

If you prefer video, I have a video version of this Java virtual threads tutorial here:

Java Virtual Threads Tutorial Video

Java Virtual Thread Diagram

Here is a diagram showing Java virtual threads - executed by platform threads - which are again executed by OS threads. While a platform thread can only execute a single virtual thread at a time, it has the ability to switch to executing a different virtual thread when the currently executed virtual thread makes a blocking call (e.g. network or concurrency data structure). This is explained in more detail in later sections of this Java virtual thread tutorial.

An overview of how Java virtual threads work.

Virtual Threads are Mounted to Platform Threads

Java virtual threads are executed by platform threads. A platform thread can only execute one virtual thread at a time. While the virtual thread is being executed by a platform thread - the virtual thread is said to be mounted to that thread.

New virtual threads are queued up until a platform thread is ready to execute it. When a platform thread becomes ready, it will take a virtual thread and start executing it.

A virtual thread that executes some blocking network call (IO) will be unmounted from the platform thread while waiting for the response. In the meantime the platform thread can execute another virtual thread.

No Time Slicing Between Virtual Threads

There is no time slicing happening between virtual threads. In other words, the platform thread does not switch between executing multiple virtual threads - except in the case of blocking network calls. As long as a virtual thread is running code and is not blocked waiting for a network response - the platform thread will keep executing the same virtual thread.

Virtual Thread Pinning

As mentioned above, a virtual thread remains mounted to a platform thread until the virtual thread makes a blocking network call - in which case the virtual thread is unmounted from the platform thread. Additionally, calling a blocking operation on e.g. a BlockingQueue will also unmount the virtual thread.

However, if the virtual thread makes a blocking file system call - that does not unmount the virtual thread. During file system calls the virtual thread remains pinned to the platform thread. That means, that the platform thread cannot execute any other virtual thread while it waits for a response from the file system. The Java virtual thread system may compensate for that, though, by starting up another platform thread to run other virtual threads being started while the file system call is going on.

There are other situations that may currently pin a virtual thread to a platform thread. For instance, entering a synchronized block. If the virtual thread makes a blocking network call from inside a synchronized block, the virtual thread may also remain pinned to the platform thread.

Which situations unmount and pin a virtual thread may change in future Java versions, so you need to keep an eye on how that evolves with new Java releases.

Creating a Java Virtual Thread

To create a new virtual thread in Java, you use the new Thread.ofVirtual() factory method, passing an implementation of the Runnable interface. Here is an example of creating a Java virtual thread:

Runnable runnable = () -> {
    for(int i=0; i<10; i++) {
        System.out.println("Index: " + i);
    }
};

Thread vThread = Thread.ofVirtual().start(runnable);

This example creates a virtual thread and starts it immediately, executing the Runnable passed to the start() method.

If you do not want the virtual thread to start immediately, you can use the unstarted() method instead. Here is an example of creating an unstarted virtual thread:

Thread vThreadUnstarted = Thread.ofVirtual().unstarted(runnable);

To start an unstarted virtual thread you just call the start() method on it, like this:

vThreadUnstarted.start();

Join a Virtual Thread

You can join a virtual thread just like a platform thread - which means waiting until the virtual thread has finished its work and stopped executing. In other words, the join() method blocks the calling thread until the virtual thread has finished its work and stopped executing. Here is an example of joining a Java virtual thread:

Thread vThread = Thread.ofVirtual().start(runnable);

vThread.join();

ExecutorService Using Virtual Threads

It is possible to create a Java ExecutorService that uses virtual threads internally. I have explained how to do that here: Creating an ExecutorService that uses virtual threads internally.

Jakob Jenkov

Featured Videos





Core Software Performance Optimization Principles

Thread Congestion in Java - Video Tutorial












Advertisements
Close TOC

All Trails

Trail TOC

Page TOC

Previous

Next