Java: Virtual Threads in JDK 21
Virtual threads, also known as fibers or green threads, are a new feature introduced in JDK 21 that allows developers to create lightweight threads that are managed by the JVM rather than the operating system. This can lead to improved performance and scalability when dealing with large numbers of threads.
What are Virtual Threads?
Virtual threads are similar to traditional threads in that they represent a sequence of instructions that can be executed concurrently with other threads. However, unlike traditional threads, virtual threads are not scheduled by the operating system but instead by the JVM. This means that the JVM can manage a large number of virtual threads with much less overhead than traditional threads.
How do Virtual Threads work?
Virtual threads are implemented using continuations, which allow the JVM to save the state of a thread and resume it later. When a virtual thread is created, it is associated with a continuation. When the virtual thread is scheduled to run, its continuation is resumed, allowing the thread to continue executing from where it left off.
One of the key benefits of virtual threads is their ability to reduce the cost of context switching. In traditional threads, a context switch requires the operating system to save and restore the state of the thread, which can be a costly operation. Virtual threads, on the other hand, are managed entirely by the JVM, so context switching is much faster and less expensive.
Another benefit of virtual threads is their ability to handle more concurrent connections than traditional threads. Virtual threads use a technique called multiplexing, which allows multiple tasks to be performed on a single thread. This means that virtual threads can handle more connections without requiring additional threads, which can help to reduce memory usage and improve application performance.
Virtual threads are also easier to manage than traditional threads, as they are managed by the JVM and do not require explicit management by the application developer. This can help to reduce the complexity of multi-threaded applications, making them easier to develop and maintain.
Example
Here is an example of how to create and use virtual threads in Java:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class VirtualThreadExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newVirtualThreadExecutor();
for (int i = 0; i < 10; i++) {
int finalI = i;
executor.submit(() -> {
System.out.println("Hello from virtual thread " + finalI);
});
}
executor.shutdown();
}
}
In this example, we create an ExecutorService using the newVirtualThreadExecutor method. This creates an executor that uses virtual threads rather than traditional threads. We then submit 10 tasks to the executor, each of which prints a message to the console. When we run this code, we see that each task is executed by a different virtual thread.
The java.util.concurrent package now includes support for virtual threads. The LockSupport API has been updated to gracefully park and unpark virtual threads, enabling APIs that use LockSupport, such as Locks, Semaphores, and blocking queues, to function seamlessly with virtual threads. The Executors.newThreadPerTaskExecutor(ThreadFactory) and Executors.newVirtualThreadPerTaskExecutor() methods provide an ExecutorService that creates a new thread for each task, facilitating the migration and interoperability with existing code that uses thread pools and ExecutorService.
- Using LockSupport with Virtual Threads: In this example, we create a virtual thread that prints a message to the console, then parks using LockSupport. We then pause the main thread for 1 second and unpark the virtual thread, causing it to resume execution and print another message to the console.
import java.util.concurrent.locks.LockSupport;
public class VirtualThreadExample {
public static void main(String[] args) throws InterruptedException {
Thread virtualThread = Thread.startVirtualThread(() -> {
System.out.println("Virtual thread running");
LockSupport.park();
System.out.println("Virtual thread resumed");
});
Thread.sleep(1000);
LockSupport.unpark(virtualThread);
}
}
- Using Executors.newThreadPerTaskExecutor(): In this example, we create an ExecutorService that creates a new thread for each task submitted to it. We submit two tasks to the executor, which are executed on separate threads. We then shut down the executor to free up resources.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPerTaskExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newThreadPerTaskExecutor(r -> new Thread(r, "Task Thread"));
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> System.out.println("Task 2"));
executor.shutdown();
}
}
- Using Executors.newVirtualThreadPerTaskExecutor(): In this example, we create an ExecutorService that creates a new virtual thread for each task submitted to it. We submit two tasks to the executor, which are executed on separate virtual threads. We then shut down the executor to free up resources.
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class VirtualThreadPerTaskExample {
public static void main(String[] args) {
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
executor.submit(() -> System.out.println("Task 1"));
executor.submit(() -> System.out.println("Task 2"));
executor.shutdown();
}
}
Conclusion
Virtual threads are a powerful new feature in JDK 21 that allow developers to create lightweight threads that are managed by the JVM rather than the operating system.
Overall, virtual threads are an exciting new feature in JDK 21 that offer many benefits over traditional threads. They provide a more efficient and scalable way to create threads in Java, and can help to improve the performance and reliability of multi-threaded applications.