- 3.1 Motivation for Virtual Threads
- 3.2 Virtual Thread Execution Model
- 3.3 Using Thread Class to Create Virtual Threads
- 3.4 Using Thread Builders to Create Virtual Threads
- 3.5 Using Thread Factory to Create Threads
- 3.6 Using Thread Executor Services
- 3.7 Scalability of Throughput with Virtual Threads
- 3.8 Best Practices for Using Virtual Threads
- Review Questions
3.2 Virtual Thread Execution Model
By design, virtual threads are lightweight:
They are less expensive to create and destroy than platform threads.
They require substantially less stack memory to manage their execution than platform threads.
Context switching between virtual threads is far less expensive than between platform threads.
Virtual threads are managed by the JVM scheduler during their lifetime, as opposed to platform threads that have a one-to-one mapping with OS threads and are scheduled by the operating system during their lifetime. In other words, the whole regime of virtual threads is managed by the JVM that has a pool of platform threads at its discretion, without the intervention of the operating system. Because of their nature, virtual threads are ideal for building concurrent applications based on the one-thread-per-task paradigm, allowing each task to execute in its own virtual thread.
Execution of virtual threads is illustrated in Figure 3.1a. As with platform threads, a virtual thread is first created to run a task and then scheduled to begin execution. There can be many virtual threads scheduled to begin execution ((1) in Figure 3.1a). From hereon their execution is at the discretion of the JVM.
In order to run the task in a virtual thread that is ready for execution, the JVM scheduler assigns the virtual thread to a platform thread for execution—this is called mounting the virtual thread (vt) and the designated platform thread is called the carrier thread (ct) ((2) and (5) in Figure 3.1a showing virtual threads vt0, vt12, and vt11 that are mounted on carrier threads ct1, ct2, and ct3, respectively). A platform thread is mapped to an OS thread (ost) and acts as a carrier thread when it is executing a virtual thread.
Figure 3.1 Virtual Thread Execution Model
Executing certain operations in its task can cause a mounted virtual thread to unmount from its carrier thread—that it, to relinquish its carrier thread (as at (2) in Figure 3.1a for virtual threads vt0 and vt12). A virtual thread is unmounted when it executes a blocking operation (such as an I/O operation). It does not require any action on the part of the application to initiate unmounting when a blocking operation is executed. I/O operations and other relevant blocking operations in the APIs have been updated to work with virtual threads. The unmounted virtual thread remains blocked until its blocking operation is ready to complete ((3) in Figure 3.1a), at which point, it is unblocked and joins other virtual threads waiting to mount and thus resume execution ((4) in Figure 3.1a).
A virtual thread that completes its execution while mounted is of course unmounted and terminated ((5) in Figure 3.1a for virtual thread vt11) and its carrier thread used to mount another virtual thread that is ready to be mounted for execution.
Instead of a carrier thread being monopolized by its virtual thread until the blocked operation is ready to complete, unmounting it allows the JVM scheduler to mount another virtual thread that is ready for execution on the carrier thread.
An execution profile of a virtual thread is shown in Figure 3.1b, illustrating that the virtual thread executes when it is mounted on a carrier thread, and is unmounted and blocked on a blocking operation until the operation is ready to complete. Note that on resumption of its execution, a virtual thread may be mounted on the same carrier thread or on a different carrier thread.
The ratio m:n of m virtual threads to n platform threads is usually very high, contributing to the high-throughput of the virtual thread execution model. A task assigned to a platform thread remains assigned to the platform thread throughout its lifetime—even while it is in a waiting or a blocked state, and cannot therefore do any useful work. The task in a virtual thread also remains assigned to the same virtual thread through its lifetime, but the virtual thread may be executed on one or more platform threads, freeing the carrier thread to do other work when the virtual thread is unmounted and blocked. The virtual thread execution model results in high utilization of the platform threads by multitude of virtual threads, which counts for the high throughput of the one-thread-per-task execution model.
Platform threads are also known as classical threads or traditional threads. OS threads are also known as native threads or kernel threads.
