10 ThreadGroup¶
Creación de un grupo de hilos¶
Una funcionalidad interesante que nos ofrece Java es la posibilidad de agrupar hilos. Esto nos permite tratar los hilos de un grupo como una sola unidad a la hora de llevar a cabo ciertas operaciones con ellos. Por ejemplo, si tenemos varios hilos que estén llevando a cabo una tarea conjunta podremos agruparlos bajo un grupo de manera que con una sola llamada se puedan, por ejemplo, interrumpir todos los hilos de dicho grupo.
Java nos proporciona la clase ThreadGroup
para trabajar con grupos de hilos. Un objeto ThreadGroup
puede estar formado por objetos Thread
o por otros objetos ThreadGroup
, generando un grupo con estructura de árbol.
Si queremos crear un hilo y agregarlo a un determinado grupo de hilos existente, simplemente tendremos que especificar como primer parámetro del constructor el objeto correspondiente al grupo. El constructor de la clase Thread
está sobrecargado para este cometido con las siguientes posibles sintaxis:
Thread(ThreadGroup grupo, Runnable objeto)
.Thread(ThreadGroup grupo, Runnable objeto, String nombreHilo)
.
La clase ThreadGroup
proporciona el método interrupt()
para marcar para interrupción todos los hilos del grupo de una vez. Además, pone a nuestra disposición algunos métodos informativos, como activeCount()
, que retorna el número de hilos del grupo activos (en ejecución), list()
, que muestra por la salida estándar información sobre los hilos del grupo y enumerate(Thread[] lista)
, que nos permite almacenar en un array pasado como argumento el conjunto de hilos activos del grupo.
Por otro lado, como mencionamos cuando hablamos de UncaughtExceptionHandler
, en caso se producirse una uncaught exception dentro del método run()
de un hilo, la JVM sigue el siguiente procedimiento trata de llamar al método uncaughtException()
del objeto manejador establecido individualmente para dicho hilo. Pero si no se ha establecido en el hilo un manejador para ese tipo de excepciones, la JVM llamará al método uncaughtException(Thread t, Throwable e)
del ThreadGroup
grupo de hilos al que pertenezca el hilo. Por tanto si queremos manejar este tipo de excepciones a nivel de ThreadGroup
simplemente tendremos que sobrescribir en el TheadGroup
dicho método.
Proyecto Grupo de Hilos¶
En este proyecto crearemos 10 hilos que pertenecerán a un mismo grupo de hilos. La tarea de estos hilos será simular que se realiza una búsqueda en Internet, por lo que generaremos aleatoriamente el número de segundos que tarda en realizarse la búsqueda (y que el hilo pasará durmiendo en la simulación). Cuando cualquiera de los hilos finaliza la búsqueda, almacena el resultado en un objeto creado específicamente para ello (que en nuestra simulación contendrá el nombre del hilo que ha finalizado la búsqueda). Cuando detectemos que uno de los hilos del grupo ha finalizado interrumpiremos a todos los hilos del grupo, ya que no tiene sentido que sigan ejecutándose una vez encontrado el resultado.
public class Main {
public static void main(String[] args) {
// Crea un nuevo grupo de hilos llamado Buscadores.
ThreadGroup searchThreads = new ThreadGroup("Search threads");
Resultado result=new Resultado();
// Crea un objeto buscador.
Buscador buscador = new Buscador(result);
// Crea e inicia cinco hilos que ejecutan el mismo objeto buscador
// añadiéndolo al grupo de hilo Buscadores.
for (int i = 0; i < 10; i++) {
Thread hilo = new Thread(grupoBuscadores, buscador);
hilo.start();
// Espero 1 segundo entre la creación de un hilo y la siguiente.
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// Espera la finalización de todos los hilos del grupo.
esperarHiloGrupo(grupoBuscadores);
// Interrumpe todos los hilos del grupos.
grupoBuscadores.interrupt();
}
// Espera la finalización de algún hilo del grupo
private static void esperarHiloGrupo(ThreadGroup grupoHilos) {
// Mientras todos los hilos del grupo estén activos
// duerme durante un segundo.
while (grupoHilos.activeCount() > 9) {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// Simula una operación de búsqueda
public class Buscador implements Runnable {
private Resultado resultado; // Resultado de la búsqueda.
// Recibe el objeto donde se almacenará el resultado.
public Buscador(Resultado resultado) {
this.resultado = resultado;
}
@Override
public void run() {
String nombreHilo = Thread.currentThread().getName();
System.out.printf("Hilo %s: Comenzando búsqueda\n", nombreHilo);
try {
buscar();
// Establezce el resultado como el nombre del hilo.
resultado.setName(nombreHilo);
} catch (InterruptedException e) {
// Si el hilo es interrumpido mientras duerme se produce
// esta excepción, que se aprovecha para finalizar el hilo.
System.out.printf("Hilo %s: Ha sido interrumpido\n", nombreHilo);
// Finaliza la ejecución del hilo.
return;
}
// Informa de que este hilo ha finalizado la búsqueda.
System.out.printf("Hilo %s: Búsqueda finalizada\n", nombreHilo);
}
// Simula la operación de búsqueda durmiendo durante un número aleatorio
// de segundos (como máximo 30)
private void buscar() throws InterruptedException {
// Obtiene aleatoriamente el número de segundos que va a dormir el hilo.
Random aleatorio = new Random();
int segundos = aleatorio.nextInt(30);
// Informa al usuario y duerme.
System.out.printf("Hilo %s: La búsqueda tardará %d segundos\n",
Thread.currentThread().getName(), segundos);
// Si es interrumpido mientras duerme lanza la excepción InterruptedException.
TimeUnit.SECONDS.sleep(segundos);
}
}