Core Java

Using Executors.newVirtualThreadPerTaskExecutor() in Java

Java introduced virtual threads with Project Loom to simplify concurrency and improve the scalability of applications. Virtual threads are lightweight and have minimal overhead compared to traditional platform threads. One of the convenient ways to work with virtual threads is through the Executors.newVirtualThreadPerTaskExecutor() method. This method returns an ExecutorService that creates a new virtual thread for each submitted task, allowing for easy and efficient concurrent task execution.

In this article, we will learn about Executors.newVirtualThreadPerTaskExecutor() and how to use it to handle concurrent tasks using virtual threads.

1. Understanding Virtual Threads

Virtual threads in Java are a lightweight type of threading that lets us run many tasks at once without using a lot of system resources. Managed by the JVM, they can be created in large quantities without slowing down our program.

1.1 Overview of Executors.newVirtualThreadPerTaskExecutor()

The Executors.newVirtualThreadPerTaskExecutor() method is a factory method that provides an ExecutorService capable of executing each task in a new virtual thread. This approach is useful for applications that need to handle a large number of concurrent tasks, such as web servers, batch processors, or real-time data processing systems.

2. Example Usage

Below is an example demonstrating how to use Executors.newVirtualThreadPerTaskExecutor() to execute tasks concurrently. This example creates an ExecutorService, submits several tasks, and then shuts down the executor.

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class VirtualThreadExecutorExample {

    public static void main(String[] args) {
        // Create an ExecutorService with virtual threads
        ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor();

        // Submit tasks to the executor
        for (int i = 0; i < 10; i++) {
            int taskNumber = i;
            executorService.execute(() -> {
                Thread.Builder builder = Thread.ofVirtual().name("virtual-thread-" + taskNumber);

                Runnable task = () -> {
                };
                Thread t = builder.start(task);
                System.out.println("Executing task " + taskNumber + " on " + t.getName());
                try {
                    // Simulate task processing with sleep
                    TimeUnit.SECONDS.sleep(1);

                    t.join();
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    System.err.println("Task " + taskNumber + " was interrupted.");
                }
                System.out.println("Task " + taskNumber + " completed on " + t.getName());
            });
        }

        // Shutdown the executor
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                System.err.println("Executor did not terminate in the specified time.");
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            System.err.println("Executor termination interrupted.");
            executorService.shutdownNow();
        }

        System.out.println("All tasks completed.");
    }
}

In this example, we start by creating an ExecutorService using Executors.newVirtualThreadPerTaskExecutor(). This executor service will use virtual threads to handle task execution. We then execute ten tasks with the executor, each of which prints a message, simulates work by sleeping for one second, and prints a completion message.

Each task runs in a separate virtual thread. The use of TimeUnit.SECONDS.sleep(1) simulates a task that takes some time to complete, allowing us to see the concurrent execution of tasks.

To name the threads, we use the Thread.Builder interface, specifically Thread.ofVirtual().name("virtual-thread-" + taskNumber), to create virtual threads with custom names.

After submitting the tasks, we shut down the executor service using executorService.shutdown(). We then wait for the termination of all tasks with executorService.awaitTermination(5, TimeUnit.SECONDS). If the executor does not terminate within the specified time, we forcefully shut it down using executorService.shutdownNow().

When you run the above example, you will see the following output indicating the execution and completion of tasks.

newvirtualthreadpertaskexecutor example output

3. Using Virtual Threads to Execute Asynchronous Tasks

Virtual threads can also be used to execute asynchronous tasks efficiently. Below is an example demonstrating how to use an ExecutorService to perform three asynchronous tasks and aggregate their results.

public class AsyncVirtualThreadExample {

public static void main(String[] args) throws InterruptedException, ExecutionException {
        // Create a new virtual thread per task executor
        ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
        
        try {
            // Submit three asynchronous tasks
            Future<String> future1 = executor.submit(() -> performTask("Task 1", 2));
            Future<String> future2 = executor.submit(() -> performTask("Task 2", 3));
            Future<String> future3 = executor.submit(() -> performTask("Task 3", 1));
            
            // Aggregate results
            String result1 = future1.get();
            String result2 = future2.get();
            String result3 = future3.get();
            
            // Print results
            System.out.println("Results:");
            System.out.println(result1);
            System.out.println(result2);
            System.out.println(result3);
        } finally {
            // Shutdown the executor
            executor.shutdown();
        }
    }
    
    private static String performTask(String taskName, int durationInSeconds) {
        try {
            // Simulate a blocking task
            Thread.sleep(durationInSeconds * 1000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return taskName + " was interrupted";
        }
        return taskName + " completed after " + durationInSeconds + " seconds";
    }
}

In this example, three tasks are submitted to the executor using Future<String> future1 = executor.submit(() -> performTask("Task 1", 2));. Each task simulates a blocking operation by sleeping for a specified number of seconds. The get() method is called on each Future to block and wait for the task’s completion. The results are then aggregated.

When you run the above application, it performs three asynchronous tasks using virtual threads, simulating blocking operations with Thread.sleep(). Each task is submitted to the executor and executed independently. Here is the expected output:

Results:
Task 1 completed after 2 seconds
Task 2 completed after 3 seconds
Task 3 completed after 1 seconds

4. Conclusion

In this article, we explored using Executors.newVirtualThreadPerTaskExecutor() for concurrent task execution with virtual threads, featuring an example of basic task execution and another example of handling asynchronous tasks with Future. In conclusion, virtual threads provide a scalable and efficient solution for managing concurrent tasks, making them a valuable feature in Java applications.

5. Download the Source Code

This article provided an example of using newVirtualThreadPerTaskExecutor().

Download
You can download the full source code of this example here: newvirtualthreadpertaskexecutor example

Omozegie Aziegbe

Omos holds a Master degree in Information Engineering with Network Management from the Robert Gordon University, Aberdeen. Omos is currently a freelance web/application developer who is currently focused on developing Java enterprise applications with the Jakarta EE framework.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button