package com.nkondrat.learning;import java.util.ArrayList;import java.util.List;import java.util.concurrent.Callable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Future;public class ReusableThreadPoolExampleMain { private final ExecutorService executorService; public ReusableThreadPoolExampleMain(int threadPoolSize) { this.executorService = Executors.newFixedThreadPool(threadPoolSize); } public void executeTasksAndWaitForCompletion(final List<Callable<String>> tasks) throws InterruptedException { // Submit all tasks and wait for completion final List<Future<String>> futures = executorService.invokeAll(tasks); // Optionally, process the results or handle exceptions for (Future<String> future : futures) { try { final String result = future.get();// This will rethrow any exception caught during task execution System.out.println("Result in for loop: " + result); } catch (Exception e) { // Handle exception e.printStackTrace(); } } } // Use this method to shut down the executor when it's no longer needed public void shutdown() { executorService.shutdown(); } static class ExampleTask implements Callable<String> { private final int taskId; public ExampleTask(int taskId) { this.taskId = taskId; } @Override public String call() throws Exception { Thread.sleep(1000); // Task implementation System.out.println("Executing task-" + taskId); return String.format("Task-%d", taskId); } } public static void main(String[] args) throws InterruptedException { ReusableThreadPoolExampleMain threadPool = new ReusableThreadPoolExampleMain(3); List<Callable<String>> tasks = new ArrayList<>(); for (int i = 0; i < 10; i++) { tasks.add(new ExampleTask(i)); } threadPool.executeTasksAndWaitForCompletion(tasks); // The ExecutorService is still alive here and can be reused // Remember to shut down the executor when it's no longer needed // threadPool.shutdown(); }}