viernes, 2 de marzo de 2012

Pool de hilos

Hoy voy hablar sobre la clase "ExecutorService", con esta clase no permite gestionar un pool de hilos. A continuación pongo un ejemplo de uso de la clase con comentarios de cada uno de los ejemplos.

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {
 private ExecutorService executor;
 
 
 /**
 *Clase para terminar todos los hilos
 */
 private void endExecutor(){
  executor.shutdown();
  try 
  {
   if (!executor.awaitTermination(60, TimeUnit.SECONDS)) 
   {
    executor.shutdownNow();
    if (!executor.awaitTermination(60, TimeUnit.SECONDS))
    {
     System.err.println("executor  no ha terminado");
    }
   }
  } catch (InterruptedException ie) {
   executor.shutdownNow();
   Thread.currentThread().interrupt();
  }
 }
 /**
  * Crea un pool que va a ir creando threads conforme se vayan necesitando
  * si hay threads libres, se ponen las tareas a procesarse dentro de dichos threads, y
  * las tareas faltantes se procesan en threads nuevos
  */
 public void newCachedThreadRool(){
  this.executor = Executors.newCachedThreadPool();
  for(int i=0; i < 5; i++){
   this.executor.execute(new Handler("Hilo_"+i));
  }
  endExecutor();
 }
 
 /**
  * crea un pool con el número de threads indicado; dichos threads siempre estarán listos 
  * para procesar tareas. El pool maneja también una cola de tareas; cada thread toma una
  * tarea de la cola y la procesa, y al terminar toma otra tarea de la cola para procesarla, etc en un ciclo
  */
 public void newFixedThreadPool(){
  this.executor = Executors.newFixedThreadPool(3);
  for(int i=0; i < 5; i++){
   this.executor.execute(new Handler("Hilo_"+i));
  }
  endExecutor();
 }
 
 /**
  * crea un pool de un solo thread, con una cola en donde se ponen las tareas a procesar; 
  * el thread toma una tarea de la cola, la procesa y toma la siguiente, en un ciclo. 
  */
 public void newSingleThreadExecutor(){
  this.executor = Executors.newSingleThreadExecutor();
  for(int i=0; i < 5; i++){
   this.executor.execute(new Handler("Hilo_"+i));
  }
  endExecutor();
 }
 
 /**
  * crea un pool que va a ejecutar tareas programadas cada cierto tiempo, ya sea una sola 
  * vez o de manera repetitiva. Es parecido a un timer, pero con la diferencia de que puede 
  * tener varios threads que irán realizando las tareas programadas conforme se desocupen. 
  * También hay una versión de un solo thread.
  */
 public void newScheduledThreadPool(){
  this.executor = Executors.newScheduledThreadPool(3);
  for(int i=0; i < 5; i++){
   this.executor.execute(new Handler("Hilo_"+i));
  }
  endExecutor();
 }

/**
  * Si queremos que despues de la excución nos devuelva algo tenemos que implemetar la 
  * interfaz Callabe
  */
 public void callableThread() throws ExecutionException, InterruptedException{
  this.executor = Executors.newCachedThreadPool();
  Collection callableResults = new HashSet();
  for(int i=0; i < 3; i++){
   callableResults.add(new CallableResult("Callable_"+i));
  }
  //solo uno
  Future future = executor.submit((CallableResult)callableResults.toArray()[0]);
  //devuelve el resultado de una tare completa correcamente
  String result = executor.invokeAny(callableResults);
  //Espera a que se termine todos los hilos
  List<"Future"> futures = executor.invokeAll(callableResults);
  
  System.out.println("future.get() = " + future.get());
  System.out.println("Result invoke any = " + result);
  for(int i=0; i < futures.size(); i++){
   System.out.println("Result invoke all = " + futures.get(i).get());
  }
 }

 public static void main(String[] args) {
  try
  {
   System.out.println("El numero de procesadores : "+Runtime.getRuntime().availableProcessors());
   new Main().callableThread();
  }catch (Exception e) {
   System.out.println(e.getMessage());
  }
 }

}
Cuando copies el código teneies que citar la comillas doble Future porque no me daba un error el editor de html. A continuación la clase Handler
import java.util.Random;

public class Handler implements Runnable {
 private String nombreHilo; 
 private Random generador;
 private int dormir;

 public Handler(String name) {
  this.nombreHilo = name;
  this.generador = new Random();
  this.dormir = generador.nextInt(5000);
 }

 public void run() {
  try 
  {
   System.out.println("\t Voy a dormir "+this.dormir+" : "+nombreHilo);
   Thread.sleep(this.dormir);
  }
  catch (InterruptedException exception) {
   exception.printStackTrace();
  }
  System.out.println("\t Ya he terminado de dormir : "+nombreHilo);
 } 
}


 public static void main(String[] args) {
  try
  {
   System.out.println("El numero de procesadores : "+Runtime.getRuntime().availableProcessors());
   new Main().callableThread();
  }catch (Exception e) {
   System.out.println(e.getMessage());
  }
 }

}
Y por ultimo la clase CallableResult
import java.util.Random;
import java.util.concurrent.Callable;

public class CallableResult implements Callable{
 private String nombreHilo; 
 private Random generador;
 private int dormir;

 public CallableResult(String name) {
  this.nombreHilo = name;
  this.generador = new Random();
  this.dormir = generador.nextInt(5000);
 }
 
 public String call() throws Exception {
  try 
  {
   System.out.println("\t Voy a dormir "+this.dormir+" : "+nombreHilo);
   Thread.sleep(this.dormir);
   System.out.println("\t Ya he terminado de dormir : "+nombreHilo);
   return "terminado "+nombreHilo;
  }
  catch (InterruptedException exception) {
   return exception.getMessage();
  }
 }
}

Con esto es todo sobre la clase ExecutorService

7 comentarios:

  1. Hola amigo, oye me gustaría saber si te puedo hacer una pregunta... y tmb saber en cuanto tiempo puedo tener respuesta de esta... Saludos

    ResponderEliminar
  2. Como haría para eliminar un solo hilo del pool despues de cierto tiempo, no lo eliminar el pool por completo sino un hilo

    Saludos

    ResponderEliminar
  3. Hola wmercado

    Para poder para un hilo después de cierto tiempo porque esta tardando demasiado o es un bucle infinito. lo que pues hacer es llamar al método cancel de la interfaz de Future Api:

    http://docs.oracle.com/javase/6/docs/api/java/util/concurrent/Future.html#cancel%28boolean%29

    Aqui te dejo un pequeño ejemplo

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future task = executor.submit(new Task());
    String str;
    try {
    str = task.get(5, TimeUnit.SECONDS);
    } finally {
    task.cancel(true);
    }

    Espero que esto resuelva tú duda

    Saludos

    ResponderEliminar
  4. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  5. muchas gracias xabe, me sirvio mucho el ejemplo, ahora tengo un problema y es que al implementar esto los hilos en cierto modo ya no son concurrentes viéndolo en el log tienen un comportamiento secuencial, ya que todos los hilo tiene que pasar el método task.get(5, TimeUnit.SECONDS) y valida el tiempo de cada task y hasta que termine ese hilo no se sigue con el otro, que mejora se puede hacer en ese aspecto que no pierda la concurrencia.

    Saludos

    ResponderEliminar
  6. Hola

    A ver si este ejemplo sirve para solucinar tu problema

    http://stackoverflow.com/questions/11915968/java-executorservice-pause-resume-a-specific-thread

    Saludos

    ResponderEliminar