Virtual Threads in Java: A Practical Guide.

Learner, Love to make things simple, Full Stack Developer, StackOverflower, Passionate about using machine learning, deep learning and AI
Search for a command to run...

Learner, Love to make things simple, Full Stack Developer, StackOverflower, Passionate about using machine learning, deep learning and AI
No comments yet. Be the first to comment.
Move beyond traditional RESTful thinking. Learn how to design APIs specifically for MCP (Model Context Protocol) servers. This guide covers the shift in mindset, a practical OpenAPI 3.1 example, and a Spring Boot implementation to make your services ...

Extending Kestra to Every Corner of Your Data Stack. Introduction: The Power of Plugins Imagine you're a master chef. You don't just have one knife - you have specialized tools for every task: a paring knife for delicate work, a chef's knife for chop...
Mastering Complex Orchestration Scenarios. Introduction: The Orchestrator's Toolkit Imagine you're conducting a symphony. You don't just wave your baton - you cue sections, adjust tempo, handle surprises, and ensure harmony. That's what advanced work...
From Data Extraction to Loading - A Practical Guide Introduction: Why ETL Still Matters in the Modern Data Stack Remember when data engineering was "extract, transform, load"? Some say ETL is dead, replaced by ELT, reverse ETL, and data mesh. But her...
Building Blocks of Declarative Orchestration. Introduction: The Power of Simplicity Imagine trying to build a house without understanding bricks, beams, and blueprints. That's what using an orchestration tool without understanding its core concepts f...
Java's introduction of Virtual Threads in Project Loom is a significant development in concurrent programming. Virtual threads are lightweight, user-mode threads designed to simplify concurrency in Java applications by enabling high levels of scalability with minimal overhead. Unlike traditional (platform) threads, virtual threads are managed by the Java Virtual Machine (JVM) rather than the operating system, allowing for millions of threads to be spawned without the resource-intensive costs of OS threads.
This guide will explore virtual threads practically, covering key concepts, setup, use cases, and practical examples to help you leverage this new concurrency model effectively.
Virtual threads are lightweight threads managed by the JVM, enabling high concurrency by decoupling Java threads from OS threads. Unlike traditional threads (known as platform threads), which are limited by system resources, virtual threads allow for massive concurrency as they are managed in user space, enabling millions of concurrent operations without overwhelming the system.
Key Features of Virtual Threads:
Minimal overhead: Each virtual thread consumes minimal memory and CPU.
Managed by JVM: Virtual threads are scheduled and managed by the JVM, reducing reliance on OS resources.
Suitable for IO-bound tasks: They can be suspended without blocking OS threads, making them ideal for applications waiting on network or file I/O.
Creating virtual threads is straightforward with Java’s new Thread.ofVirtual().start() API.
public class VirtualThreadExample {
public static void main(String[] args) {
Thread virtualThread = Thread.ofVirtual().start(() -> {
System.out.println("Running in a virtual thread: " + Thread.currentThread());
});
virtualThread.join(); // Wait for completion
}
}
Explanation: The Thread.ofVirtual().start() method creates a virtual thread, which then prints a message. The join() method ensures the main thread waits for the virtual thread to complete.
Java also provides a VirtualThreadPerTaskExecutor to manage virtual threads, ideal for cases where a pool of tasks is required.
import java.util.concurrent.Executors;
public class VirtualThreadExecutorExample {
public static void main(String[] args) {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10; i++) {
int taskId = i;
executor.submit(() -> {
System.out.println("Task " + taskId + " running on " + Thread.currentThread());
});
}
} // Automatically shuts down after use
}
}
Explanation: Here, we create an executor that spawns a new virtual thread for each task, automatically cleaning up after use. This is beneficial for executing multiple isolated tasks without managing thread creation and lifecycle manually.
Platform threads are limited by system resources and are suitable for CPU-bound tasks, while virtual threads are highly efficient for IO-bound tasks.
| Feature | Platform Thread | Virtual Thread |
| Creation Overhead | High | Low |
| Context Switching | Managed by OS | Managed by JVM |
| Best Use Case | CPU-bound tasks | IO-bound tasks |
| Memory Consumption | High | Low |
Virtual threads are perfect for applications that need to handle thousands of concurrent tasks, such as HTTP requests in a server.
import java.util.concurrent.Executors;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.URI;
public class HighConcurrencyExample {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
var executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 1000; i++) {
int requestId = i;
executor.submit(() -> {
try {
HttpRequest request = HttpRequest.newBuilder(new URI("https://example.com"))
.GET().build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Response for request " + requestId + ": " + response.body());
} catch (Exception e) {
e.printStackTrace();
}
});
}
executor.close();
}
}
Explanation: This example sends 1000 HTTP requests concurrently using virtual threads, which is efficient as each request may be suspended during network communication without occupying a platform thread.
For applications that need to read multiple files simultaneously, virtual threads can offer a performant, non-blocking solution.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.Executors;
public class FileReadExample {
public static void main(String[] args) throws IOException {
var executor = Executors.newVirtualThreadPerTaskExecutor();
String[] fileNames = {"file1.txt", "file2.txt", "file3.txt"};
for (String fileName : fileNames) {
executor.submit(() -> {
try {
String content = Files.readString(Paths.get(fileName));
System.out.println("Contents of " + fileName + ": " + content);
} catch (IOException e) {
e.printStackTrace();
}
});
}
executor.close();
}
}
Explanation: This example reads multiple files concurrently without blocking the main thread. Each file read is performed in a virtual thread, enabling efficient concurrent file processing.
Virtual threads can handle blocking operations without locking the underlying OS thread, making them suitable for high-latency tasks.
import java.util.concurrent.Executors;
public class BlockingOperationExample {
public static void main(String[] args) {
var executor = Executors.newVirtualThreadPerTaskExecutor();
executor.submit(() -> {
try {
Thread.sleep(1000); // Blocking operation
System.out.println("Completed after delay");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
executor.close();
}
}
Explanation: Here, a virtual thread handles a blocking Thread.sleep() operation without occupying an OS thread. This allows for thousands of such tasks without taxing system resources.
High-Concurrency Web Services: Virtual threads allow handling of thousands of concurrent requests, ideal for scalable REST or WebSocket services.
Event-Driven Microservices: In event-driven architectures, virtual threads can process events concurrently without blocking.
Concurrent Database Queries: Use virtual threads to run multiple database queries in parallel for data aggregation tasks.
Batch Processing Applications: Process large datasets concurrently, such as reading data files or applying transformations to large data streams.
Avoid CPU-Intensive Tasks: Virtual threads excel at IO-bound tasks but may not improve performance for CPU-bound operations.
Careful with Legacy Code: Integrating virtual threads into older codebases may require refactoring, especially if the code is platform thread-dependent.
Error Handling: Virtual threads handle exceptions in the same way as traditional threads; however, error handling becomes more important when working with high concurrency.
Java’s virtual threads provide a powerful alternative to traditional concurrency models by allowing the creation of high-performance, scalable applications. They’re particularly suited for applications with high levels of concurrency requirements, such as web servers, microservices, and any IO-bound workloads. Through practical examples, we’ve seen how virtual threads simplify concurrency in Java by reducing the resource constraints associated with traditional threads.
More such articles:
https://www.youtube.com/@maheshwarligade