Cómo usar Thread.join

Thread.join es un método que se puede utilizar para esperar a que un hilo termine su ejecución, o al menos intentarlo. Existen varias formas de llamar a Thread.join y en esta lección te muestro en qué se diferencia cada una de las invocaciones que tiene el método.

Con el método join() de la clase Thread, puedes esperar a que un hilo termine de ejecutarse. Más o menos, ya que hay varias formas de usar este método. En esta lección te voy a contar cómo usar cada uno de los métodos de join() y qué consecuencias tiene usar uno u otro.

Cómo usar Thread.join()

En primer lugar, el más simple de todos es join() sin parámetros. Este es un método de la clase Thread, así que lo tienes que aplicar sobre un hilo cuya referencia tengas. Imagina que has creado un hilo mediante:

var hilo = new Thread(new ProcesoLargaDuracion());

Podrías ahora llamar al método join() de la clase hilo para esperar a que el hilo hilo termine de ejecutarse:

System.out.println("Me espero");

hilo.join()

System.out.println("He terminado");

Cuando llames a join(), lo que ocurrirá es que el hilo desde el que llames precisamente al método join() se quedará a la espera, hasta que el hilo sobre el cual llamaste a join() termine. Al igual que ocurre con otros métodos como Thread.sleep(), llamar a join() provoca que el hilo desde el que llamas a ese método deje de recibir tiempo de CPU, al menos hasta que el hilo que esperas termine. Después de todo, ¿para qué quieres recibir tiempo de CPU si no vas a poder hacer nada? Simplemente, esa llamada a join() no terminará hasta entonces, y el hilo quedará congelado en el tiempo.

Thread.join() con tiempo límite

El inconveniente de llamar a Thread.join() es que si el hilo no termina por un error de programación, o si el hilo se va a quedar mucho tiempo trabajando, puede que el otro hilo también pase ahora demasiado tiempo bloqueado. Por ejemplo, si esperamos a que se complete un proceso de 4 o 5 minutos, eso son 4 o 5 minutos que el hilo interesado también va a quedar bloqueado.

Para no estar tanto tiempo esperando, una cosa que podemos hacer es pasarle un timeout o tiempo límite a Thread.join. Con este timeout lo que hacemos es decirle el tiempo máximo que queremos esperar a que el hilo termine. Si transcurrido ese tiempo, el hilo todavía no ha terminado, la ejecución de Thread.join() concluye igualmente, aunque sin haber logrado su propósito, que es esperar a que el hilo termine.

Tienes que tener en cuenta que Thread.join(long) en este caso no te va a reportar si el hilo al que esperabas ha llegado a terminar o no, así que deberás consultar su estado, o llamar a isAlive(), para saber esto. isAlive() devuelve verdadero si el hilo está en ejecución, y falso si el hilo ya no está en ejecución. Podrías hacer algo como esto:

public void detener(Thread objetivo) {
  System.out.print("Deteniendo hilo ");
  do {
    objetivo.join(500);
    System.out.print(".");
  } while (objetivo.isAlive());
}

En este caso, detendremos igualmente el hilo, porque nos vamos a quedar a la espera de que isAlive() deje de devolver verdadero, lo que indicaría que el hilo ya ha terminado. Sin embargo, en vez de quedarnos infinitamente a la espera, cada 500 milisegundos el método join() termina su ejecución para que podamos poner un punto y así al menos confirmar que no es que el programa se haya bloqueado, es que todavía estamos esperando.

Thread.join con un objeto Duration

Por último, una forma moderna de llamar a Thread.join() es pasándole como parámetro un dato de tipo Duration, que codifique de forma más clara el tiempo que queremos que se quede a la espera. Por ejemplo, Duration.ofSeconds(2) implicaría 2 segundos, o Duration.ofMinutes(1) implicaría 1 minuto.

Lamentablemente, Java es peculiar, y este método tiene una interfaz diferente al resto. En este caso, ese Thread.join() sí que devolverá un valor booleano que indica si el hilo que hemos intentado detener se ha llegado a detener, así que no será necesario estar llamando a isAlive() porque la propia llamada a Thread.join() lo hará en nuestro lugar y nos informará del resultado.

¿InterruptedException?

Sí, join() es otro método donde se puede lanzar un InterruptedException si mientras estabamos esperando a que termine de ejecutarse un hilo, el hilo que espera recibe una interrupción. Al igual que ocurre con Thread.sleep(), la biblioteca estándar querrá interrumpir la ejecución de la espera para que nuestro programa pueda pasar a hacer otra cosa.

En el caso del Thread.join() que espera infinitamente, esta sería la única circunstancia en la que el método join() podría dejar de ejecutarse sin que haya terminado el hilo al cual estábamos esperando.

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