package ar.com.javacuriosities.concurrency.cached_thread_pool; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; /* * Executors.newCachedThreadPool(): Crea un pool de threads conforme se vallan necesitando. * Si hubiera thread libres se reutilizan. * Si llegan 5 Threads se crearán 5 hilos para tratar las tareas, si a los 5 segundos * llegan 15 tareas mas, si hubiera hilos disponible se reutilizan y * llegan a faltar hilos se crean mas * Los hilos que estan inactivos mucho tiempo se terminan automáticamente, así que * si llegaran nuevas tareas nuevos hilos seran creados. */ public class Main { private static final int NUMBER_OF_TASKS = 10; public static void main(String[] args) { // Creamos la cache de Threads ExecutorService executor = Executors.newCachedThreadPool(); // Definimos una lista que contendrán los resultados futuros List<Future<Integer>> futures = new ArrayList<>(); /* * Creamos las tareas y hacemos submit de cada una, cada submit devuelve un Future con el mismo * generic type que el Callable, sobre le Future podremos ejecutar el método get() para obtener el resultado de callable cuando este haya terminado */ for (int initialTaskId = 0; initialTaskId < NUMBER_OF_TASKS; initialTaskId++) { futures.add(executor.submit(new MyCustomCallable(initialTaskId))); } // Recorremos las tareas y pedimos los valores cuando terminan for (Future<Integer> future : futures) { try { System.out.println("Thread finishes: " + future.get()); } catch (InterruptedException | ExecutionException e) { // Log and Handle exception e.printStackTrace(); } } /* * El método shutdown le dice al executor que no reciba mas tarea * y que finalize las tareas pendientes que pueda llegar a tener */ executor.shutdown(); } /* * Clase que implementa de Callable. * Callable es una Interface similar a Runnable, pero a diferencia de Runnable, su único método call() * devuelve un resultado (Del tipo que queramos) y permite lanzar una excepción. * El parámetro que devuelve el método se define por medio de Generic */ private static final class MyCustomCallable implements Callable<Integer> { private int workerNumber; public MyCustomCallable(int workerNumber) { this.workerNumber = workerNumber; } @Override public Integer call() { for (int i = 0; i <= 100; i += 20) { System.out.println("Thread Number: " + workerNumber + ", percentage complete: " + i); try { TimeUnit.MILLISECONDS.sleep((long) (Math.random() * 100)); } catch (InterruptedException e) { // Log and Handle exception e.printStackTrace(); } } return workerNumber; } } }