A preview feature of Java 19 includes Virtual Threads, a compact implementation of Java threads. High-throughput concurrent applications can be written, maintained, and observed with a great deal fewer work thanks to virtual threads. The well-known thread-per-request style of programming is given fresh life by virtual threads, enabling it to scale with almost ideal hardware use.
Virtual Threads can be supported by existing applications and libraries with no modification because they are fully compatible with the current "Thread" API. By supporting the current debugging and profiling interfaces, virtual threads make it simple to troubleshoot, debug, and profile virtual threads using current tools and methods.
The primary output of OpenJDK's Project Loom, virtual threads are introduced to the Java platform for the first time in Java 19; this is the key deliverable. This is simultaneously one of the biggest improvements to Java in a long time and a hardly noticeable change. Virtual Threads radically alter how the Java runtime communicates with the underlying operating system, removing major barriers to scalability, but they make only minor adjustments to how we create and manage concurrent systems. Virtual Threads behave almost identically to the threads we are used to, and there is hardly any additional API surface. There is more unlearning than learning to be done to use virtual threads successfully.
Virtual Threads
A different Java.lang.Thread implementation is virtual threads. Threads that use Java's garbage-collected heap to store their stack frames rather than the large operating system-allocated memory chunks. The memory footprint for a virtual thread starts at just a few hundred bytes and extends and contracts automatically as the call stack grows and shrinks, so we don't have to anticipate how much stack space a thread would need or make a general estimate for all threads.
Platform threads, which remain the unit of scheduling, are the only threads that the operating system is aware of. The Java runtime makes arrangements for code to execute on a virtual thread by mounting it on a platform thread known as a carrier thread. When a virtual thread is mounted, the necessary stack frames are temporarily copied from the heap to the carrier thread's stack, and the virtual thread borrows the carrier stack while it is mounted. Code running in a virtual thread can be unmounted from the carrier thread, and any updated stack frames copied are returned to the heap, enabling the carrier thread to work on anything else when it might otherwise block due to IO, locking, or other resource availability (such as running another virtual thread.) Virtual Threads are unmounted from their carriers rather than blocking when a blocking operation on them is encountered in almost all of the blocking places in the JDK.
There is not much to understand to use virtual threads because they are just threads and have minimal additional API surface of their own. But to use them successfully, there are quite a few things we need to relearn.
- Everyone Out of the Pool.
- Overuse of ThreadLocal
Everyone Out of the Pool
The patterns of thread production are the hardest to unlearn. Java developers have learned that it is generally far better to let ExecutorService manage and pool threads in a policy-driven way than to create threads directly. Java 5 introduced the java.util.concurrent package, which includes the ExecutorService framework. But pooling turns into an antipattern when dealing with virtual threads. (We don't have to stop using ExecutorService or the encapsulation of policy that it offers; we can acquire an ExecutorService that produces a new virtual thread per task by using the new factory function Executors::newVirtualThreadPerTaskExecutor )
Because they are so lightweight, virtual threads can be created even for brief operations, and attempting to reuse or recycle them is pointless. Virtual Threads were created specifically to handle brief tasks like an HTTP fetch or a JDBC query.
Overuse of ThreadLocal
As a result of virtual threads, libraries could also need to modify how they use ThreadLocal. The usage of ThreadLocal to cache resources that are expensive to allocate, not thread-safe, or just to prevent repeated allocation of a frequently used object is one of its occasional (and, some would argue, misuse) applications (e.g., ASM uses a ThreadLocal to maintain a per-thread char[] buffer, used for formatting operations.) This is an ad-hoc kind of pooling; if these objects need to be pooled, they should be pooled explicitly. A ThreadLocal is used to amortize the production cost of a costly resource across numerous activities that might execute in the same thread.
API & Platform Changes
A preview feature is virtual threads and the accompanying APIs. This means that to support virtual threads, the-enable-preview switch is required.
There is no new VirtualThread base type because virtual threads are just java.lang.Thread implementations. But there are several new API points for creating and analyzing threads that have been added to the Thread API. A new Thread.Builder class, new factory methods for Thread::ofVirtual and Thread::ofPlatform, and Thread::startVirtualThread to start a task on a virtual thread in one step are all included. Although they are only used to create platform threads, the old thread builders continue to function as before.
Virtual and platform threads behave differently in a few specific ways. Virtual threads are always daemon threads; they are unaffected by the Thread::setDaemon function. Virtual threads have primacy at all times. NORM PRIORITY, which is unchangeable. Some (defective) legacy techniques, such as ThreadGroup and the Thread methods halt, suspend, and delete, are not supported by virtual threads. A thread's virtuality can be determined using the function Thread::isVirtual.
Preparing the JDK
Although Project Loom's major output is virtual threads, the JDK has undergone several enhancements to guarantee that programs will use virtual threads effectively.
- New socket implementations: To better support virtual threads, JEP 353 (Reimplement the Legacy Socket API) and JEP 373 (Reimplement the Legacy DatagramSocket API) replaced the versions of Socket, ServerSocket, and DatagramSocket (including making blocking methods interruptible in virtual threads.)
- Virtual-thread-awareness: Virtual threads are now known to almost all blocking sites in the JDK, and they will unmount a virtual thread instead of stopping it.
- Revisiting the use of ThreadLocal: Because of the anticipated shifting thread usage patterns, many ThreadLocal uses in the JDK were revised.
- Revisiting locking: Critical intrinsic locks were replaced with ReentrantLock because acquiring an intrinsic lock (synchronised) currently pins a virtual thread to its carrier. (In the future, the relationship between virtual threads and intrinsic locks will likely be enhanced.)
- Improved thread dumps: Greater control over thread dumps is offered to filter out virtual threads, group relevant virtual threads together, or create dumps in machine-readable forms that can be post-processed for improved observability.
Even while virtual threads are Project Loom's major focus, several Loom side projects improve on virtual threads. The first is a straightforward framework for organized concurrency, which provides a potent way to manage and coordinate collaborating teams of virtual threads. Extent local variables are another option; they are comparable to thread locals but are better suited (and more efficient) for use with virtual threads.
Thanks for reading! In this blog, we focus on how to use Java Virtual Threads and their Importance in Java Programming. If you are looking for Java development services we got your back, at Sanesquare Technologies we provide the best Java development services. Feel free to contact us to get a free consultation.
Does your Project Demand Expert Assistance?
Contact us and let our experts guide you and fulfil your aspirations for making the project successful
