Virtual thread in Java

In the tutorial Initializing and running a thread in Java, I introduced you to Java’s Thread class to create a new thread. Threads created by this Thread class are called platform threads and the number of platform threads that can be created is limited, depending on the specification of the machine you are using to run the Java application. From version 19, Java introduces a new feature called virtual thread that allows us to create a large number of threads, used similarly to platform threads. Virtual threads will be managed by the JRE, useful in cases of dealing with blocking IO processing. How is it in detail? We will find out together in this tutorial!

First, to distinguish between the initialization of platform threads and virtual threads, Java introduces two static() methods in the Thread class, ofVirtual() and ofPlatform() to initialize these two types of threads.

For example, I have a task implemented using the Runnable interface with the following content:

I can create a virtual thread and platform thread to submit this task as follows:

Result:

As you can see, both types of threads can execute tasks and return the same results.

In the Calculator class above, I have a line of code to print the name of the Thread that is processing the task, but as you can see, for virtual threads, we cannot get the name of this Thread. That’s because, by default, virtual threads do not have a defined name. You can define a name for the virtual thread as follows:

Now you will see the name of the virtual thread displayed as follows:

You can also create a new virtual thread using the static startVirtualThread() method of the Thread class or newVirtualThreadPerTaskExecutor() of the Executors class.

The static startVirtualThread() method of the Thread class will initialize a new virtual thread and call the start() method to execute the task. I can create a new virtual thread and submit tasks to this virtual thread with the startVirtualThread() method as follows:

Run the application, you will see the following results:

Using the static newVirtualThreadPerTaskExecutor() method of the ExecutorService class, you can do the same thing:

Result:

As I said above, we can create a large number of virtual threads instead of being limited like platform threads.

With the same code, if using platform threads, you can only create a certain number of new threads, for example as follows:

In the code above, I loop 1 million times and each time I loop I will create a new platform thread and start it. With my machine, I can only create 2025 platform threads, and get an out-of-memory error afterward, as follows:


If I now use a virtual thread instead:

You will see that you can create 1 million new virtual threads without any problem:

Internally, a pool of platform threads is used to run virtual threads.

Our task is executed using the platform thread, via a virtual thread.

When performing a blocking IO task, to avoid blocking platform threads, virtual threads should be used.

The virtual thread is executed using the Continuation class. This Continuation class uses the run() method to execute our task in a virtual thread. While waiting for a blocking IO task to complete, the virtual thread will put our task in heap memory, releasing the platform thread it is using for other tasks.

When the IO task completes, a signal will be triggered to call Continuation.run(). This Continuation.run() will retrieve the virtual thread’s stack from the heap memory and put it on the platform thread’s waiting list to get the thread and resume the task. The new thread can be different from the original thread the task used to run.

Virtual threads should only be used for blocking IO tasks, not for computational tasks!

Add Comment