Java virtual threads is a new feature introduced in JDK 19. It has potential to improve an applications availability, throughput and code quality on top of reducing memory consumption. This post intends to introduce java virtual threads in an easily understandable manner.
Video: To see the visual walk-through of this post, click below:
Thread Life Cycle
Fig: Typical thread’s life cycle
Let’s walkthrough a typical lifecycle of a thread:
1. Thread is created in a thread pool
2. Thread waits in the pool for a new request to come
3. Once the new request comes, the thread picks up the request and it makes a backend Database call to service this request.
4. Thread waits for response from the backend Database
5. Once response comes back from the Database, the thread processes it, and sends back the response to customer
6. Thread is returned back to the thread pool
Step #2 to #6 will repeat until the application is shutdown.
If you notice, the thread is actually doing real work only in step #3 and #5. In all other steps(i.e., step #1, step #2, step #4, step #6), it is basically waiting(or doing nothing). In most applications, a significant number of threads predominantly waits during most of its lifetime.
Platform threads architecture
Fig: Each Platform Thread is mapped to an Operating System Thread
In the previous release of JVM(Java Virtual Machine), there was only one type of thread. It’s called as ‘classic’ or ‘platform’ thread. Whenever a platform thread is created, an operating system thread is allocated to it. Only when the platform thread exits(i.e., dies) the JVM, this operating system thread is free to do other tasks. Until then, it cannot do any other tasks. Basically, there is a 1:1 mapping between a platform thread and an operating system thread.
According to this architecture, OS thread will be unnecessarily locked down in step #1, step #2, step #4, step #6 of the thread’s life cycle, even though it’s not doing anything during these steps. Since OS threads are precious and finite resources, it’s time is extensively wasted in this platform threads architecture.
Virtual threads architecture
Fig: OS Threads are not allocated to Platform threads until real work needs to be executed
Fig: Virtual Threads are mapped to platform threads when it does real work
In order to efficiently use underlying operating system threads, virtual threads have been introduced in JDK 19. In this new architecture, a virtual thread will be assigned to a platform thread (aka carrier thread) only when it executes real work. As per the above-described thread’s life cycle, only during step #3 and step #5 virtual thread will be assigned to the platform thread(which in turn uses OS thread) for execution. In all other steps, virtual thread will be residing as objects in the Java heap memory region just like any of your application objects. Thus, they are lightweight and more efficient.
What is ‘stack chunk object’?
When a virtual thread is not executing a real work and resides in a Java heap memory region, it is called as ‘stack chunk object’.
How to create virtual threads?
All the APIs that work with current platform threads, will work with virtual threads as is. However, the APIs to create java virtual threads are slightly different. Below is a sample program that creates Java virtual thread:
1: 2: Runnable task = () -> { System.out.println("Hello Virtual Thread!"); }; 3: 4: Thread.startVirtualThread(task);
In this sample program, the virtual thread is created using ‘Thread.startVirtualThread()‘ API. This is the easiest API to create Java virtual threads. 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.
You can also create java virtual threads using the following APIs:
1. Thread.ofVirtual().start(Runnable);
2. Thread.ofVirtual().unstarted(Runnable);
3. Executors.newVirtualThreadPerTaskExecutor();
4. Executors.newThreadPerTaskExecutor(ThreadFactory);
More details about these APIs can be found in this post.
Advantages of Java virtual threads
Because of it’s elegant architecture, virtual threads provides several advantages:
1. Improves application availability
2. Improves application throughput
3. Reduces ‘OutOfMemoryError: unable to create new native thread’
4. Reduces application memory consumption
5. Improves code quality
6. 100% compatible with Platform Threads
To learn more about these advantages in detail, you may read this post.
What is the performance impact of virtual threads?
Java virtual threads are much more lightweight than platform threads. To learn more about java virtual threads performance impact, you may refer to studies done here. However, in a nutshell:
If your application either have lot of threads or large stack size (i.e. -Xss), then switching to virtual threads would reduce your application’s memory consumption.
If your application is creating new threads frequently (instead of leveraging thread pool), switching to virtual threads can improve your application’s response time.
Conclusion
We hope this post helped to gain better understanding about java virtual threads – a wonderful addition to our phenomenal Java programming language.