Ir al contenido principal

Paralelismo en JAVA Executors (ExecutorService, Callables y Futures).


Código fuente articulo:
https://www.dropbox.com/s/jci67120hmd0uce/Paralelismo.zip?dl=0

Para manejo de concurrencia Java desde la versión 5 presento el Concurrency API  este presento una mejora substancial en el manejo de hilos y procesos en paralelo, antes solo contabas con Thread y Runnable. lo que te obligaba a controlar la creación y el numero de hilos de ejecución, no te entregaba un resultado del procesamiento y no te dejaba controlar las Excepciones que lanzara un hilo...un hilo se lanzaba y amenos que le enviaras un un CallBack perdías todo contacto con el.
Para solucionar este problema se crearon dos tipos de objetos Callables y Futures. estos dos te permiten encapsular una tarea asignándole un tipo de Objeto que sera el valor de retorno y hacer seguimiento a las tareas que ejecuto en paralelo pudiendo preguntar si ya termino, que resultado lanzo y que excepciones ocurrieron.

Arranquemos con la implementación de Callable, Callable es una interfaz que te permite definir que
clase regresara la ejecución de una tarea y se ve así;

public abstract interface java.util.concurrent.Callable<T>{ ....

cuando la implementes quedara así

public class CallableDuerme implements Callable<String> {

Implementar esta interfaz de obliga a implementar el metodo call(), este metodo sera el que llamara el Executor para completar la tarea.

@Override
 public String call() throws Exception {....

si te fijas la implementación por defecto añade un Exception , si recuerdas de POO cuando
implementes el metodo puedes no incluir ninguna Exception

@Override
 public String call() {....

O ser mas especifico en la Exception que voy a manejar, es decir cualquier Exception  que herede de
Exception  como IOException o InterruptedException

@Override
 public String call() throws InterruptedException {

En nuestro ejemplo vamos a utilizar InterruptedException  porque vamos a dormir las tareas un rato mientras se ejecutan.

entonces nuestra implementacion de Callable, va a ser una clase que tenga una propiedad nombre para saber que tarea fue la que se termino y que va a dormir un tiempo entre 0 y 20 segundos para simular que esta haciendo algo. y se vera así:

public class CallableDuerme implements Callable<String> {

 private String nombre;

 public CallableDuerme(String nombre) {

  this.nombre = nombre;
 }
 @Override
 public String call() throws InterruptedException {

  Random ran= new Random();
  TimeUnit.SECONDS.sleep(ran.nextInt(20));
  return "Termine de dormir soy:"+nombre;
 }
}

Ya tenemos la tarea ahora definiremos el objeto que se encargara de ejecutar nuestra tareas en n cantidad de hilos así:

ExecutorService executor = Executors.newFixedThreadPool(2);

crearemos un arreglo de Futures donde guardaremos la referencia a nuestras tareas para poder recuperar el resultado de su ejecución.

List<Future<String>> future= new ArrayList<>();

después crearemos tres tareas que enviaremos a ejecutar en nuestro executor y que reverenciaremos
con un futuro y guardaremos las referencias en una lista.

future.add(executor.submit(new CallableDuerme("Juanita")));
  future.add(executor.submit(new CallableDuerme("Marrrrria")));
  future.add(executor.submit(new CallableDuerme("Daniela")));

ahora nos quedaremos monitoreando nuestras tareas hasta que todas terminen:

int listos=0;

  while(listos<3){
   listos=0;
   for(Future<String> resultado:future){
 
    if(resultado.isDone()){
     listos++;
     try {
      System.out.println(resultado.get());
     } catch (InterruptedException | ExecutionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
    System.out.println(listos);
 
   }
  }

Al final nuestra clase se vera así

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
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;

public class CallableDuerme implements Callable<String> {

 private String nombre;

 public CallableDuerme(String nombre) {

  this.nombre = nombre;
 }
 @Override
 public String call() throws InterruptedException {

  Random ran= new Random();
  TimeUnit.SECONDS.sleep(ran.nextInt(20));
  return "Termine de dormir soy:"+nombre;
 }
 public static void main(String[] args) {

  ExecutorService executor = Executors.newFixedThreadPool(2);
  List<Future<String>> future= new ArrayList<>();

  future.add(executor.submit(new CallableDuerme("Juanita")));
  future.add(executor.submit(new CallableDuerme("Marrrrria")));
  future.add(executor.submit(new CallableDuerme("Daniela")));

  int listos=0;

  while(listos<3){
   listos=0;
   for(Future<String> resultado:future){
 
    if(resultado.isDone()){
     listos++;
     try {
      System.out.println(resultado.get());
     } catch (InterruptedException | ExecutionException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
     }
    }
    System.out.println(listos);
 
   }
  }

 
 }

}

Ahora ya tienes lo básico de Executors, sabes que es un Callable y tienes las herramientas para recuperar el resultado de la ejecución de las tareas con los Futures. ya lo sabes todo, ahora sal a buscarte la vida y no te andes quejando.

Comentarios