Revolutionized Concurrency ? Introduction to Java 21 Virtual Threads.
Threads
has been the atomic unit of concurrency in Java for generations. Via various methods like Runnable
, Thread
, ExecutorService
etc, Java devs have been utilizing the concurrency capabilities of Java.
But a main concern of the Thread/Concurrency implementation in Java was that the concurrency unit of Java, a Thread, having a 1:1 dependency on a Operating System (OS) thread. For example, if we start a thread in Java , for the entire lifetime of that Thread, a thread in the operating system would be tied to it. So, even if our java Thread is blocked due to a I/O operation, the related OS thread would be blocked too. The major downside of this is that OS threads are memory intensive and costly to create. So if we have 1000 Java Threads, 1000 Operating System threads would be used. And before no-time, the Operating System Threads would reach their limit and because of that the Java Thread creation/Concurrency capabilities would be capped, without even utilizing the full might of the hardware which the code is running on.
These Operating System Threads are known to be the first resource to run out when running a concurrency intensive application, even before the hardware limitations are met.
This would be very noticeable on a Java Application Server, which typically creates one thread per one request. So imagine a request comes to the application server and it fetches a record from a database, until the database operation is done and a response is sent, the thread which that request is running on would be blocked and reserved. If thousands of requests like that come at the same time, the Operating System Threads would run out and the application server would stall.
Virtual Threads to the rescue !
That’s when virtual threads comes into the rescue. Virtual threads brings the concept of user-mode threads to Java. Via Virtual Threads, a m:n mapping between Operating System Threads and Java Threads are created (m Virtual Threads are mapped to n OS Threads).
In simple terms, a Java Virtual Thread would not be bound to a particular OS thread. Instead of that, multiple Virtual Threads would be allocated to one OS thread. In that way, a very large number of Virtual Threads could be spin up by utilizing only a limited number of OS threads.
In code level, a Virtual Thread is an instance of java.Lang.Thread
which is not tightly bound to a OS Thread.
As seen above VirtualThread
class is a child of the Thread type. In this context, the traditional threads which are bound to a OS Thread are called Platform Threads
.
In the way Virtual Threads are implemented, they only consume the OS Threads when CPU time is needed for calculations. In other times when the Virtual Thread is blocked (DB Queries/ IO Operations etc.) , it will be detached from a OS thread and will be in a suspended state, hence freeing up valuable OS Threads.
Because of their lightweight nature, unlike Platform Threads, Virtual Threads are very cheap to create and can be created in very large numbers. So unlike Platform Threads, Thread Pooling is not necessary to handle multiple tasks, a separate virtual thread could be created for each task.
Scheduling
Platform Threads and Virtual Threads are implemented in very different ways in the thread scheduling aspect.
A Platform Thread is scheduled by the OS Scheduler but in Virtual Threads’ case, JDK has implemented a separate Scheduler which will schedule threads onto the OS Threads.
As I’ve said before Virtual Threads would unmount from a carrier OS Thread when the Virtual Thread is in the blocked state. And after the blocking task is finished, the Virtual Thread would be mounted on a totally separate OS Thread by the JDK Thread Scheduler.
Code Example
Now lets look at a simple example about how to create a Virtual Thread in Java.
public class App {
public static void main(String[] args) {
ThreadFactory virtualThreadfactory = Thread.ofVirtual().factory();
Thread thread = factory.newThread(() -> {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
Here , a Java Thread Factory is created using the Thread.ofVirtual().factory()
method. By using this virtual thread factory, multiple Virtual Threads could be spin up and used. There is a platform thread version of this function too, which is written as Thread.ofPlatform().factory().
Apart from that, various methods have been introduced to create Virtual Threads accordingly to your need. Find some other ways we could create a Virtual Thread in Java, below.
// Using Java Executors to create V. Threads.
var executor = Executors.newVirtualThreadPerTaskExecutor();
executor.submit(Runnable);
// creates a new v. thread and schedules it to be executed
Thread t1 = Thread.ofVirtual.start(Runnable);
// creates a new v. thread. start() method must be called separately.
Thread t2 = Thread.ofVirtual.unstarted(Runnable);
// creates a v. thread and schedules it to be executed.
Thread t3 = Thread.startVirtualThread(Runnable);
Conclusion
Virtual Threads are a lightweight re-imagination of Java threads which are highly scalable and plentiful. By utilizing these, we could implement highly efficient software with great latency/throughout.
This has been a simple introduction to Java Virtual Threads and lets talk about this topic in detail in a later article.
I hope you gained something after reading this article. Follow for more Java related blogs. Cheers !
Want to Connect ?
- Medium
- LinkedIn
- Twitter(X)
- Threads
Find me everywhere @rashm1n.
Resources
https://openjdk.org/jeps/444