Skip to content

7 Esperar la finalización

Esperar la finalización de un hilo

Imaginemos que diseñamos un programa que lanza varios hilos que llevan a cabo tareas de inicialización del programa. Una vez iniciados estos hilos, el hilo principal del programa deberá esperar a que concluya la ejecución de los mismos para poder continuar con su función, ya que es estrictamente necesario que se haya llevado a cabo completamente la inicialización antes de seguir con otras tareas. Es evidente que en este caso el hilo principal debe esperar la finalización de los otros hilos.

Para este cometido la clase Thread() dispone del método join(). Cuando ejecutamos este método sobre un objeto hilo, se suspende la ejecución del hilo llamador hasta que el objeto hilo sobre el que hemos hecho join() concluye su ejecución. De esta manera podríamos decir que el hilo llamador espera la unión (join) del hilo llamado. Por ejemplo, si desde el hilo principal de la aplicación se realiza la llamada hilo2.join(), el hilo principal de la aplicación suspende su ejecución hasta que finaliza la ejecución de hilo2.

Sincronización final

join() supone un punto de sincronización con la terminación de otro hilo

Java proporciona una sobrecarga adicional del método join() con el formato join(long milisegundos), que permite que la espera no sea indefinida, sino que si el hilo llamado no ha finalizado aún su ejecución en el número de milisegundos indicados, el hilo llamador continúa su ejecución. Por ejemplo, si desde el hilo principal de la aplicación se realiza la llamada hilo2.join(3000), el hilo principal de la aplicación suspende su ejecución hasta que finaliza la ejecución de hilo2 o hasta que hayan transcurrido 3 segundos (lo que ocurra antes). Si pasamos un valor negativo como tiempo máximo de espera se lanza la excepción IllegalArgumentException. Si pasamos el valor 0 como tiempo máximo de espera, se esperará indefinidamente la finalización del hilo sobre el que se ha hecho join().

Como ocurre con la mayoría de los métodos bloqueantes (que bloquean un hilo), si el un hilo ha sido marcado para interrupción y trata de hacer join() sobre otro hilo, o si es marcada para interrupción mientras está bloqueado esperando a que finalice el hilo sobre el que ha hecho join(), se lanzará inmediatamente la excepción InterruptedException sin esperar a que finalice el hilo sobre el que ha hecho join().

No debemos olvidar que cualquier hilo puede esperar la finalización de otro hilo, no sólo el hilo principal.

Proyecto Join

En el siguiente proyecto crearemos una aplicación que muestra la tabla de multiplicar de los números desde el 1 al 10, usando para cada tabla un hilo de ejecución distinto. Una vez lanzados los hilos, el hilo principal esperará su finalización, tras lo cual mostrará un mensaje en pantalla informativo de que ha concluido la operación.

public class Main {

    private static final int LAST_NUMBER = 10;

    public static void main(String[] args) throws InterruptedException {
        Thread[] threads = new Thread[LAST_NUMBER + 1];
        for (int i = 1; i <= LAST_NUMBER; i++) {
            threads[i-1] = new Thread(new MultiplicationTable(i), "Table of " + i);
            threads[i-1].start();
        }
        for (int i = 1; i <= LAST_NUMBER; i++) {
            threads[i-1].join();
        }
        System.out.println("All multiplication tables printed");
    }

}
class MultiplicationTable implements Runnable {

    private final int number;

    MultiplicationTable(int number) {
        this.number = number;
    }

    @Override
    public void run() {
        for (int i = 1; i <= 10; i++) {
            System.out.printf("%s: %d * %d = %d\n", Thread.currentThread()
                    .getName(), number, i, i * number);
        }
    }

}

Si ejecutamos el programa veremos que hasta que no hayan terminado de ejecutarse todos los hilos secundarios el hilo principal no mostrará el mensaje de que todas las tablas se han imprimido.