ExecutorService with Virtual Threads in Java

Introduction to Virtual Threads

Virtual Threads (Project Loom), introduced in Java 19 as a preview and finalized in Java 21, are lightweight threads that dramatically reduce the overhead of thread creation and management. They enable highly concurrent applications without the limitations of platform threads.


1. Basic Virtual Threads Usage

Creating Virtual Threads

import java.util.concurrent.*;
public class VirtualThreadsBasic {
public static void main(String[] args) throws Exception {
// Method 1: Using Thread.startVirtualThread()
Thread virtualThread = Thread.startVirtualThread(() -> {
System.out.println("Hello from virtual thread: " + Thread.currentThread());
});
virtualThread.join();
// Method 2: Using Thread.ofVirtual()
Thread.Builder virtualBuilder = Thread.ofVirtual().name("worker-", 0);
Thread vt1 = virtualBuilder.start(() -> {
System.out.println("Virtual thread 1: " + Thread.currentThread().getName());
});
Thread vt2 = virtualBuilder.start(() -> {
System.out.println("Virtual thread 2: " + Thread.currentThread().getName());
});
vt1.join();
vt2.join();
// Method 3: Using Executors.newVirtualThreadPerTaskExecutor()
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
Future<String> future = executor.submit(() -> {
Thread.sleep(1000);
return "Result from virtual thread";
});
System.out.println("Future result: " + future.get());
}
}
}

Comparing Platform vs Virtual Threads

import java.util.concurrent.*;
import java.util.stream.*;
public class ThreadComparison {
public static void main(String[] args) throws Exception {
int taskCount = 10_000;
System.out.println("=== Platform Threads ===");
testWithPlatformThreads(taskCount);
System.out.println("\n=== Virtual Threads ===");
testWithVirtualThreads(taskCount);
}
private static void testWithPlatformThreads(int taskCount) throws Exception {
long startTime = System.currentTimeMillis();
try (var executor = Executors.newFixedThreadPool(200)) {
var futures = IntStream.range(0, taskCount)
.mapToObj(i -> executor.submit(() -> {
Thread.sleep(100); // Simulate work
return i;
}))
.toList();
// Wait for all tasks
for (var future : futures) {
future.get();
}
}
long duration = System.currentTimeMillis() - startTime;
System.out.printf("Completed %d tasks in %d ms%n", taskCount, duration);
}
private static void testWithVirtualThreads(int taskCount) throws Exception {
long startTime = System.currentTimeMillis();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var futures = IntStream.range(0, taskCount)
.mapToObj(i -> executor.submit(() -> {
Thread.sleep(100); // Simulate work
return i;
}))
.toList();
// Wait for all tasks
for (var future : futures) {
future.get();
}
}
long duration = System.currentTimeMillis() - startTime;
System.out.printf("Completed %d tasks in %d ms%n", taskCount, duration);
}
}

2. ExecutorService with Virtual Threads

Basic Virtual Thread Executor

import java.util.concurrent.*;
import java.util.*;
public class VirtualThreadExecutorBasics {
public static void main(String[] args) throws Exception {
// 1. Virtual Thread Per Task Executor (most common)
try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int taskId = i;
Future<String> future = executor.submit(() -> {
String threadName = Thread.currentThread().getName();
System.out.printf("Task %d executed by %s%n", taskId, threadName);
Thread.sleep(1000); // Simulate I/O operation
return "Task-" + taskId + "-completed";
});
futures.add(future);
}
// Collect results
for (Future<String> future : futures) {
System.out.println("Result: " + future.get());
}
}
// 2. Custom Virtual Thread Factory
ThreadFactory virtualThreadFactory = Thread.ofVirtual().factory();
try (ExecutorService customExecutor = 
Executors.newThreadPerTaskExecutor(virtualThreadFactory)) {
customExecutor.submit(() -> {
System.out.println("Custom virtual thread: " + Thread.currentThread());
});
}
}
}

Advanced Virtual Thread Configuration

import java.util.concurrent.*;
import java.util.*;
public class AdvancedVirtualThreadExecutor {
public static void main(String[] args) throws Exception {
// Custom virtual thread factory with configuration
ThreadFactory customVirtualThreadFactory = Thread.ofVirtual()
.name("custom-vt-", 0)
.inheritInheritableThreadLocals(false) // Don't inherit thread locals
.factory();
try (ExecutorService executor = 
Executors.newThreadPerTaskExecutor(customVirtualThreadFactory)) {
// Submit multiple tasks
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 5; i++) {
final int taskId = i;
tasks.add(() -> {
String threadName = Thread.currentThread().getName();
System.out.printf("Task %d on %s%n", taskId, threadName);
// Simulate different types of work
if (taskId % 2 == 0) {
Thread.sleep(500); // I/O bound
} else {
// CPU-bound work (virtual threads still work, but no benefit)
long result = 0;
for (int j = 0; j < 1000; j++) {
result += j;
}
}
return "Task-" + taskId + "-done";
});
}
// Invoke all tasks
List<Future<String>> futures = executor.invokeAll(tasks);
// Process results
for (Future<String> future : futures) {
System.out.println("Completed: " + future.get());
}
}
}
}

3. Real-World Use Cases

HTTP Client with Virtual Threads

import java.net.http.*;
import java.net.URI;
import java.util.concurrent.*;
import java.util.*;
public class HttpClientVirtualThreads {
private final HttpClient httpClient;
private final ExecutorService virtualThreadExecutor;
public HttpClientVirtualThreads() {
this.httpClient = HttpClient.newBuilder()
.executor(Executors.newVirtualThreadPerTaskExecutor())
.build();
this.virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
}
public CompletableFuture<String> fetchUrlAsync(String url) {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
return httpClient.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::body);
}
public List<String> fetchMultipleUrls(List<String> urls) throws Exception {
List<Callable<String>> tasks = new ArrayList<>();
for (String url : urls) {
tasks.add(() -> {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(url))
.GET()
.build();
HttpResponse<String> response = httpClient.send(
request, HttpResponse.BodyHandlers.ofString());
return String.format("URL: %s, Status: %d, Length: %d",
url, response.statusCode(), response.body().length());
});
}
// Execute all HTTP requests concurrently
return virtualThreadExecutor.invokeAll(tasks).stream()
.map(future -> {
try {
return future.get();
} catch (Exception e) {
return "Error: " + e.getMessage();
}
})
.toList();
}
public void shutdown() {
virtualThreadExecutor.shutdown();
}
public static void main(String[] args) throws Exception {
HttpClientVirtualThreads fetcher = new HttpClientVirtualThreads();
List<String> urls = List.of(
"https://httpbin.org/delay/1",  // Simulates 1-second delay
"https://httpbin.org/delay/2",  // Simulates 2-second delay
"https://httpbin.org/delay/1",
"https://httpbin.org/status/200",
"https://httpbin.org/status/404"
);
long startTime = System.currentTimeMillis();
List<String> results = fetcher.fetchMultipleUrls(urls);
long duration = System.currentTimeMillis() - startTime;
System.out.printf("Fetched %d URLs in %d ms%n", urls.size(), duration);
results.forEach(System.out::println);
fetcher.shutdown();
}
}

Database Operations with Virtual Threads

import java.sql.*;
import java.util.concurrent.*;
import java.util.*;
import java.util.concurrent.atomic.*;
public class DatabaseVirtualThreads {
private final ExecutorService virtualThreadExecutor;
private final AtomicInteger connectionCounter;
public DatabaseVirtualThreads() {
this.virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
this.connectionCounter = new AtomicInteger(0);
}
// Simulate database connection
private Connection getConnection() throws SQLException {
// In real application, this would be a real database connection
System.out.println("Getting connection: " + connectionCounter.incrementAndGet());
Thread.sleep(100); // Simulate connection overhead
return null; // Simplified for example
}
public CompletableFuture<List<Map<String, Object>>> queryUsersAsync(List<Integer> userIds) {
List<CompletableFuture<Map<String, Object>>> futures = userIds.stream()
.map(userId -> CompletableFuture.supplyAsync(() -> {
try {
return queryUserById(userId);
} catch (SQLException e) {
throw new CompletionException(e);
}
}, virtualThreadExecutor))
.toList();
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]))
.thenApply(v -> futures.stream()
.map(CompletableFuture::join)
.toList());
}
private Map<String, Object> queryUserById(int userId) throws SQLException {
System.out.printf("Querying user %d on virtual thread: %s%n", 
userId, Thread.currentThread().getName());
// Simulate database I/O
Thread.sleep(200);
Map<String, Object> user = new HashMap<>();
user.put("id", userId);
user.put("name", "User-" + userId);
user.put("email", "user" + userId + "@example.com");
user.put("thread", Thread.currentThread().getName());
return user;
}
public void batchProcessUsers(List<Integer> userIds) throws Exception {
List<Callable<String>> tasks = userIds.stream()
.map(userId -> (Callable<String>) () -> {
try {
Map<String, Object> user = queryUserById(userId);
// Simulate additional processing
Thread.sleep(100);
return String.format("Processed user %d: %s", 
userId, user.get("name"));
} catch (Exception e) {
return "Failed for user " + userId + ": " + e.getMessage();
}
})
.toList();
List<Future<String>> futures = virtualThreadExecutor.invokeAll(tasks);
for (Future<String> future : futures) {
System.out.println(future.get());
}
}
public void shutdown() {
virtualThreadExecutor.shutdown();
}
public static void main(String[] args) throws Exception {
DatabaseVirtualThreads processor = new DatabaseVirtualThreads();
// Generate test user IDs
List<Integer> userIds = new ArrayList<>();
for (int i = 1; i <= 50; i++) {
userIds.add(i);
}
System.out.println("=== Batch Processing Users ===");
long startTime = System.currentTimeMillis();
processor.batchProcessUsers(userIds);
long duration = System.currentTimeMillis() - startTime;
System.out.printf("\nProcessed %d users in %d ms%n", userIds.size(), duration);
processor.shutdown();
}
}

4. Error Handling and Resource Management

Structured Concurrency with Virtual Threads

import java.util.concurrent.*;
import java.util.*;
public class StructuredConcurrencyVirtualThreads {
public static class TaskResult {
private final String taskName;
private final String result;
private final Throwable error;
public TaskResult(String taskName, String result, Throwable error) {
this.taskName = taskName;
this.result = result;
this.error = error;
}
public boolean isSuccess() { return error == null; }
public String getTaskName() { return taskName; }
public String getResult() { return result; }
public Throwable getError() { return error; }
}
public List<TaskResult> executeTasksWithStructuredConcurrency(List<String> tasks) 
throws InterruptedException {
try (var scope = new StructuredTaskScope.ShutdownOnFailure();
var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<StructuredTaskScope.Subtask<String>> subtasks = new ArrayList<>();
// Submit all tasks
for (String task : tasks) {
StructuredTaskScope.Subtask<String> subtask = scope.fork(() -> 
executeTask(task, executor));
subtasks.add(subtask);
}
try {
// Wait for all tasks or first failure
scope.join();
scope.throwIfFailed(); // Throws if any task failed
} catch (ExecutionException e) {
System.out.println("One or more tasks failed: " + e.getMessage());
}
// Collect results
List<TaskResult> results = new ArrayList<>();
for (StructuredTaskScope.Subtask<String> subtask : subtasks) {
if (subtask.state() == StructuredTaskScope.Subtask.State.SUCCESS) {
results.add(new TaskResult(
"Task", subtask.get(), null
));
} else if (subtask.state() == StructuredTaskScope.Subtask.State.FAILED) {
results.add(new TaskResult(
"Task", null, subtask.exception()
));
}
}
return results;
}
}
private String executeTask(String taskName, ExecutorService executor) {
try {
// Simulate different types of tasks
if (taskName.contains("slow")) {
Thread.sleep(2000);
} else if (taskName.contains("error")) {
throw new RuntimeException("Simulated error for: " + taskName);
} else {
Thread.sleep(500);
}
return taskName + "-completed";
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Task interrupted: " + taskName, e);
}
}
public static void main(String[] args) throws Exception {
StructuredConcurrencyVirtualThreads executor = 
new StructuredConcurrencyVirtualThreads();
List<String> tasks = List.of(
"fast-task-1", "fast-task-2", "slow-task-1", 
"error-task", "fast-task-3"
);
List<TaskResult> results = executor.executeTasksWithStructuredConcurrency(tasks);
System.out.println("\n=== Task Execution Results ===");
for (TaskResult result : results) {
if (result.isSuccess()) {
System.out.printf("✓ %s: %s%n", result.getTaskName(), result.getResult());
} else {
System.out.printf("✗ %s: %s%n", result.getTaskName(), result.getError().getMessage());
}
}
}
}

Resource Cleanup with Virtual Threads

import java.util.concurrent.*;
import java.util.*;
import java.util.concurrent.locks.*;
public class ResourceManagementVirtualThreads {
private final ExecutorService virtualThreadExecutor;
private final Set<AutoCloseable> resources;
private final ReentrantLock resourceLock;
public ResourceManagementVirtualThreads() {
this.virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
this.resources = ConcurrentHashMap.newKeySet();
this.resourceLock = new ReentrantLock();
}
public <T> CompletableFuture<T> executeWithResource(
Callable<T> task, AutoCloseable resource) {
// Register resource for cleanup
resources.add(resource);
return CompletableFuture.supplyAsync(() -> {
try {
T result = task.call();
return result;
} catch (Exception e) {
throw new CompletionException(e);
} finally {
// Always cleanup resource
cleanupResource(resource);
}
}, virtualThreadExecutor);
}
private void cleanupResource(AutoCloseable resource) {
resourceLock.lock();
try {
if (resources.remove(resource)) {
try {
resource.close();
System.out.println("Resource cleaned up: " + resource);
} catch (Exception e) {
System.err.println("Error cleaning up resource: " + e.getMessage());
}
}
} finally {
resourceLock.unlock();
}
}
public void shutdown() {
// Cleanup any remaining resources
resourceLock.lock();
try {
for (AutoCloseable resource : resources) {
try {
resource.close();
} catch (Exception e) {
System.err.println("Error during shutdown cleanup: " + e.getMessage());
}
}
resources.clear();
} finally {
resourceLock.unlock();
}
virtualThreadExecutor.shutdown();
}
// Simulated resource
private static class SimulatedResource implements AutoCloseable {
private final String name;
private volatile boolean closed = false;
public SimulatedResource(String name) {
this.name = name;
}
public String readData() throws Exception {
if (closed) throw new IllegalStateException("Resource closed");
Thread.sleep(100); // Simulate I/O
return "Data from " + name;
}
@Override
public void close() throws Exception {
if (!closed) {
closed = true;
Thread.sleep(50); // Simulate cleanup
System.out.println("Closed resource: " + name);
}
}
@Override
public String toString() {
return "SimulatedResource{name='" + name + "', closed=" + closed + "}";
}
}
public static void main(String[] args) throws Exception {
ResourceManagementVirtualThreads manager = new ResourceManagementVirtualThreads();
List<CompletableFuture<String>> futures = new ArrayList<>();
for (int i = 0; i < 5; i++) {
SimulatedResource resource = new SimulatedResource("Resource-" + i);
CompletableFuture<String> future = manager.executeWithResource(() -> {
String data = resource.readData();
System.out.println("Processed: " + data + " on " + Thread.currentThread().getName());
return data;
}, resource);
futures.add(future);
}
// Wait for all tasks
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
System.out.println("\nAll tasks completed. Resources should be cleaned up.");
manager.shutdown();
}
}

5. Performance Monitoring and Tuning

Virtual Threads Monitoring

import java.util.concurrent.*;
import java.lang.management.*;
import java.util.*;
import java.util.concurrent.atomic.*;
public class VirtualThreadsMonitoring {
private final ExecutorService virtualThreadExecutor;
private final ThreadMXBean threadMXBean;
private final AtomicInteger tasksCompleted;
private final AtomicInteger tasksFailed;
public VirtualThreadsMonitoring() {
this.virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
this.threadMXBean = ManagementFactory.getThreadMXBean();
this.tasksCompleted = new AtomicInteger();
this.tasksFailed = new AtomicInteger();
}
public void executeMonitoredTasks(int taskCount) throws Exception {
List<CompletableFuture<Void>> futures = new ArrayList<>();
printThreadStats("Before task execution");
for (int i = 0; i < taskCount; i++) {
final int taskId = i;
CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
try {
executeMonitoredTask(taskId);
tasksCompleted.incrementAndGet();
} catch (Exception e) {
tasksFailed.incrementAndGet();
System.err.println("Task " + taskId + " failed: " + e.getMessage());
}
}, virtualThreadExecutor);
futures.add(future);
}
// Wait for all tasks
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
printThreadStats("After task execution");
printTaskStatistics(taskCount);
}
private void executeMonitoredTask(int taskId) throws InterruptedException {
long startTime = System.currentTimeMillis();
// Simulate mixed I/O and computation
if (taskId % 3 == 0) {
// I/O intensive
Thread.sleep(200);
} else if (taskId % 3 == 1) {
// Mixed
Thread.sleep(100);
doSomeComputation();
Thread.sleep(100);
} else {
// Computation with small I/O
doSomeComputation();
Thread.sleep(50);
}
long duration = System.currentTimeMillis() - startTime;
System.out.printf("Task %d completed in %d ms on %s%n", 
taskId, duration, Thread.currentThread().getName());
}
private void doSomeComputation() {
// Simulate CPU work
long result = 0;
for (int i = 0; i < 1000; i++) {
result += i * i;
}
}
private void printThreadStats(String phase) {
System.out.println("\n=== " + phase + " ===");
System.out.println("Platform threads: " + threadMXBean.getThreadCount());
System.out.println("Peak threads: " + threadMXBean.getPeakThreadCount());
// Virtual thread stats (Java 21+)
try {
// This would require accessing internal APIs in real implementation
System.out.println("Virtual threads: [monitoring not directly available]");
} catch (Exception e) {
// Virtual thread monitoring might not be directly available
}
}
private void printTaskStatistics(int totalTasks) {
System.out.println("\n=== Task Statistics ===");
System.out.println("Total tasks: " + totalTasks);
System.out.println("Completed: " + tasksCompleted.get());
System.out.println("Failed: " + tasksFailed.get());
System.out.println("Success rate: " + 
(tasksCompleted.get() * 100.0 / totalTasks) + "%");
}
public void shutdown() {
virtualThreadExecutor.shutdown();
}
public static void main(String[] args) throws Exception {
VirtualThreadsMonitoring monitor = new VirtualThreadsMonitoring();
try {
monitor.executeMonitoredTasks(100);
} finally {
monitor.shutdown();
}
}
}

6. Best Practices and Patterns

Virtual Threads Best Practices

import java.util.concurrent.*;
import java.util.*;
public class VirtualThreadsBestPractices {
// 1. Use for I/O-bound tasks, not CPU-bound tasks
public void ioBoundWork() throws Exception {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Callable<String>> ioTasks = List.of(
() -> { Thread.sleep(100); return "Database query result"; },
() -> { Thread.sleep(150); return "HTTP API response"; },
() -> { Thread.sleep(200); return "File read completed"; }
);
List<Future<String>> results = executor.invokeAll(ioTasks);
for (Future<String> result : results) {
System.out.println(result.get());
}
}
}
// 2. Avoid synchronized blocks with virtual threads
public void useReentrantLockInsteadOfSynchronized() throws Exception {
ReentrantLock lock = new ReentrantLock();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Callable<String>> tasks = new ArrayList<>();
for (int i = 0; i < 10; i++) {
final int taskId = i;
tasks.add(() -> {
lock.lock();
try {
// Critical section
Thread.sleep(50);
return "Task " + taskId + " completed";
} finally {
lock.unlock();
}
});
}
executor.invokeAll(tasks).forEach(future -> {
try {
System.out.println(future.get());
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
// 3. Use CompletableFuture for complex async workflows
public CompletableFuture<String> complexAsyncWorkflow() {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
return CompletableFuture
.supplyAsync(() -> fetchUserData(), executor)
.thenApplyAsync(userData -> validateUserData(userData), executor)
.thenComposeAsync(validData -> processUserData(validData), executor)
.exceptionally(throwable -> {
System.err.println("Workflow failed: " + throwable.getMessage());
return "Fallback result";
});
}
}
// 4. Proper resource cleanup with try-with-resources
public void properResourceManagement() throws Exception {
// ExecutorService implements AutoCloseable in virtual thread executors
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
executor.submit(() -> {
// Simulate work with resources
try (var connection = getDatabaseConnection();
var statement = connection.createStatement()) {
Thread.sleep(100);
System.out.println("Work completed with proper resource management");
} catch (Exception e) {
throw new RuntimeException(e);
}
});
} // Executor automatically shutdown here
}
// 5. Batch operations for better throughput
public void batchOperations(List<String> items) throws Exception {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
int batchSize = 10;
List<List<String>> batches = new ArrayList<>();
for (int i = 0; i < items.size(); i += batchSize) {
batches.add(items.subList(i, Math.min(i + batchSize, items.size())));
}
List<Callable<String>> batchTasks = batches.stream()
.map(batch -> (Callable<String>) () -> processBatch(batch))
.toList();
List<Future<String>> results = executor.invokeAll(batchTasks);
for (Future<String> result : results) {
System.out.println("Batch result: " + result.get());
}
}
}
// Helper methods
private String fetchUserData() {
try { Thread.sleep(100); } catch (InterruptedException e) { }
return "User data";
}
private String validateUserData(String data) {
try { Thread.sleep(50); } catch (InterruptedException e) { }
if (data == null) throw new IllegalArgumentException("Invalid data");
return data + " (validated)";
}
private CompletableFuture<String> processUserData(String data) {
return CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(100); } catch (InterruptedException e) { }
return data + " (processed)";
}, Executors.newVirtualThreadPerTaskExecutor());
}
private AutoCloseable getDatabaseConnection() {
return new AutoCloseable() {
@Override
public void close() throws Exception {
System.out.println("Connection closed");
}
};
}
private String processBatch(List<String> batch) {
try { 
Thread.sleep(100); 
return "Processed batch of " + batch.size() + " items";
} catch (InterruptedException e) { 
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
VirtualThreadsBestPractices practices = new VirtualThreadsBestPractices();
System.out.println("=== I/O Bound Work ===");
practices.ioBoundWork();
System.out.println("\n=== Batch Operations ===");
List<String> items = new ArrayList<>();
for (int i = 0; i < 25; i++) {
items.add("Item-" + i);
}
practices.batchOperations(items);
System.out.println("\n=== Complex Async Workflow ===");
practices.complexAsyncWorkflow()
.thenAccept(result -> System.out.println("Final result: " + result))
.join();
}
}

Summary

Key Benefits of Virtual Threads:

  1. Massive Concurrency: Create millions of virtual threads efficiently
  2. Simplified Code: Write blocking code without thread pool management
  3. Better Resource Utilization: Optimal for I/O-bound workloads
  4. Platform Integration: Work with existing Java APIs seamlessly

Best Practices:

  • Use for I/O-bound tasks, not CPU-bound computation
  • Avoid synchronized blocks - use ReentrantLock instead
  • Use try-with-resources for ExecutorService cleanup
  • Leverage Structured Concurrency for task management
  • Monitor and tune based on application needs

Common Use Cases:

  • HTTP servers handling many concurrent requests
  • Database applications with many concurrent connections
  • Microservices with high concurrency requirements
  • Batch processing of I/O-intensive tasks
  • Async/await patterns with simplified code

Performance Characteristics:

  • Low memory footprint (~2KB per virtual thread vs ~1MB per platform thread)
  • Fast creation/destruction (microseconds vs milliseconds)
  • Efficient I/O operations with automatic yielding
  • Compatible with existing code and libraries

Virtual Threads represent a fundamental shift in Java concurrency, making highly concurrent applications much simpler to write and maintain while providing exceptional performance for I/O-bound workloads.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper