Introducción al uso de ExecutorService en Java

Una vez que sabemos lo que es un Executor, vamos a ver en este vídeo cómo obtener un ExecutorService. Lo haremos mediante la clase de apoyo Executors, que nos permite fabricar instancias de un ExecutorService al que le podremos asignar tanto Runnables como Callables.

Para usar un ExecutorService en Java tendremos dos formas de hacerlo.

  • Por suerte, ya existen implementaciones de ExecutorService y de ScheduledExecutorService, así que no tendremos que hacerla desde cero. Por ejemplo, ThreadPoolExecutorService es un tipo de ExecutorService que utiliza un pool de hilos (de esto hablo en la lección siguiente).
  • Sin embargo, una forma más simple de usar un ExecutorService va a ser emplear los métodos de la clase Executors. Estos métodos permiten instanciar rápidamente un tipo de ExecutorService en función de las necesidades que tengamos.

Por ejemplo, puedo usar el siguiente código Java para obtener un ExecutorService:

ExecutorService svc = Executors.newSingleThreadExecutor();

En este caso, he pedido un single thread executor. Un executor de este modo sólo usará un único hilo, así que todas las tareas que encolemos mediante el método submit se harán una tras otra. A modo de ejemplo, podríamos encolar un grupo de tareas:

Ahora podríamos encolar estas tareas mediante el método submit:

for (int i = 0; i < 5; i++) {
  final String taskId = "Tarea" + i;
  svc.submit(() -> {
    System.out.println(taskId + ": Ejecutando");
    try {
      Thread.sleep(2000);
      System.out.println(taskId + ": Ejecutada");
    } catch (InterruptedException e) {
      System.out.println(taskId + ": Interrumpida");
    }
  });
}

Cuando se obtiene un ExecutorService, está listo para aceptar tareas inmediatamente, por lo que estas comenzarán a ejecutarse. Así que invocar el programa sin agregar código adicional, provocará que las tareas se ejecuten:

Tarea0: Ejecutando
Tarea0: Ejecutada
Tarea1: Ejecutando
Tarea1: Ejecutada
Tarea2: Ejecutando
Tarea2: Ejecutada
Tarea3: Ejecutando
Tarea3: Ejecutada
Tarea4: Ejecutando
Tarea4: Ejecutada

En este caso, notamos varias cosas:

  • El orden de ejecución de las tareas va a ser secuencial. Hasta que una no termina, no empieza la siguiente. Además, las tareas parece que van en orden. Aunque no se puede garantizar esto, tiene sentido que se ejecuten en orden ya que se usa un único hilo para ejecutar tareas, por lo que el resultado es bastante secuencial.
  • Notarás que el programa no termina. Esto es porque instanciar un ExecutorService podría provocar que se fabriquen hilos, por lo que cuando el hilo principal termine, la máquina se quedará en ejecución debido a que hay otros hilos que continúan ejecutándose: los del propio ExecutorService.

Para impedir que el programa se quede pensando infinitamente, se debe utilizar el método shutdown() cuando ya no vayamos a enviarle más tareas:

svc.shutdown();

Cuando hagas eso, y opcionalmente esperes mediante awaitTermination() a que termine todo lo que tiene abierto, notarás que ya no se queda el programa infinitamente en ejecución luego de terminar el hilo principal y la última tarea que ahora mismo se esté ejecutando.

Estrategias de ExecutorService

Cuando usamos los métodos de la clase Executors, puede que no tengamos mucha información sobre cómo se están ejecutando esas tareas que encolemos, pero una de las estrategias más habituales que nos vamos a encontrar va a ser la estrategia ThreadPool. Detallaré en la siguiente lección qué es un thread pool.

La cuestión aquí es que en muchos de estos casos, los hilos que se generen por usar un ExecutorService se considerarán workers (trabajadores). Son hilos reusables que siempre están tratando de ejecutar código. Un hilo que actúe como worker no se crea ni se destruye con tanta frecuencia y eso permite ahorrar recursos.

Por lo tanto, por ir adquiriendo vocabulario técnico, podríamos empezar a decir que cuando usamos un ExecutorService, tenemos un servicio en el que los workers empiezan a aceptar tasks.

Lista de reproducción
  1. 1
    ¿Qué es la concurrencia?
    8 minutos
  2. 2
    ¿Qué es un hilo?
    5 minutos
  3. 3
    Crear un hilo en Java
    10 minutos
  4. 4
    Interrupción de hilos
    10 minutos
  5. 5
    Cómo usar Thread.join
    14 minutos
  6. 6
    Corrupción de memoria
    8 minutos
  7. 7
    Monitores y synchronized
    10 minutos
  8. 8
    Bloque synchronized
    12 minutos
  9. 9
    Preguntas típicas sobre synchronized en Java
    8 minutos
  10. 10
    Interbloqueos, synchronized y el problema de la cena de los filósofos
    13 minutos
  11. 11
    Introducción a Executor en Java
    11 minutos
  12. 12
    Introducción al uso de ExecutorService en Java
    14 minutos
  13. 13
    Introducción a Thread Pools en Java
    10 minutos
  14. 14
    Cómo crear Thread Pools en Java
    11 minutos
  15. 15
    ¿Cómo funcionan wait() y notify()?
    10 minutos
  16. 16
    Ejemplo de wait() y notify() en Java
    9 minutos
  17. 17
    La palabra clave volatile
    8 minutos
  18. 18
    Cómo usar Future
    8 minutos
  19. 19
    Variables atómicas
    7 minutos