Tutorial de Java

Grupos de Hilos

Anterior | Siguiente
Todo hilo de ejecución en Java debe formar parte de un grupo. La clase ThreadGroup define e implementa la capacidad de un grupo de hilos.

Los grupos de hilos permiten que sea posible recoger varios hilos de ejecución en un solo objeto y manipularlo como un grupo, en vez de individualmente. Por ejemplo, se pueden regenerar los hilos de un grupo mediante una sola sentencia.

Cuando se crea un nuevo hilo, se coloca en un grupo, bien indicándolo explícitamente, o bien dejando que el sistema lo coloque en el grupo por defecto. Una vez creado el hilo y asignado a un grupo, ya no se podrá cambiar a otro grupo.

Si no se especifica un grupo en el constructor, el sistema coloca el hilo en el mismo grupo en que se encuentre el hilo de ejecución que lo haya creado, y si no se especifica en grupo para ninguno de los hilos, entonces todos serán miembros del grupo "main", que es creado por el sistema cuando arranca la aplicación Java.

En la ejecución de los ejemplos de esta sección, se ha podido observar la circunstancia anterior. Por ejemplo, el resultado en pantalla de uno de esos ejemplos es el que se reproduce a continuación:

% java java1002
Thread[hiloA,5,main]
Thread[hiloB,5,main]
Thread[main,5,main]

Como resultado de la ejecución de sentencias del tipo:

System.out.println( Thread.currentThread() );

Para presentar la información sobre el hilo de ejecución. Se puede observar que aparece el nombre del hilo, su prioridad y el nombre del grupo en que se encuentra englobado.

La clase Thread proporciona constructores en los que se puede especificar el grupo del hilo que se esta creando en el mismo momento de instanciarlo, y también métodos como setThreadGroup(), que permiten determinar el grupo en que se encuentra un hilo de ejecución.

Arrancar y Parar Threads

Ahora que ya se ha visto por encima como se arrancan, paran, manipulan y agrupan los hilos de ejecución, el ejemplo un poco más gráfico, java1004.java, implementa un contador.

El progama arranca un contador en 0 y lo incrementa, presentando su salida tanto en la pantalla gráfica como en la consola. Una primera ojeada al código puede dar la impresión de que el programa empezará a contar y presentará cada número, pero no es así. Una revisión más profunda del flujo de ejecución del applet, revelará su verdadera identidad.

En este caso, la clase java1004 está forzada a implementar Runnable sobre la clase Applet que extiende. Como en todos los applets, el método init() es el primero que se ejecuta. En init(), la variable contador se inicializa a cero y se crea una nueva instancia de la clase Thread. Pasándole this al constructor de Thread, el nuevo hilo ya conocerá al objeto que va a correr. En este caso this es una referencia a java1004. Después de que se haya creado el hilo, necesitamos arrancarlo. La llamada a start(), llamará a su vez al método run() de la clase, es decir, a java1004.run(). La llamada a start() retornará con éxito y el hilo comenzará a ejecutarse en ese instante. Observar que el método run() es un bucle infinito. Es infinito porque una vez que se sale de él, la ejecución del hilo se detiene. En este método se incrementará la variable contador, se duerme 10 milisegundos y envía una petición de refresco del nuevo valor al applet.

Es muy importante dormirse en algún lugar del hilo, porque sino, el hilo consumirá todo el tiempo de la CPU para su proceso y no permitirá que entren métodos de otros hilos a ejecutarse. Otra forma de detener la ejecución del hilo sería hacer una llamada al método stop(). En el contador, el hilo se detiene cuando se pulsa el ratón mientras el cursor se encuentre sobre el applet. Dependiendo de la velocidad del ordenador, se presentarán los números consecutivos o no, porque el incremento de la variable contador es independiente del refresco en pantalla. El applet no se refresca a cada petición que se le hace, sino que el sistema operativo encolará las peticiones y las que sean sucesivas las convertirá en un único refresco. Así, mientras los refrescos se van encolando, la variable contador se estará todavía incrementando, pero no se visualiza en pantalla.

El uso y la conveniencia de utilización del método stop() es un poco dudoso y algo que quizá debería evitarse, porque puede haber objetos que dependan de la ejecución de varios hilos, y si se detiene uno de ellos, puede que el objeto en cuestión estuviese en un estado no demasiado consistente, y si se le mata el hilo de control puede que definitivamente ese objeto se dañe. Una solución alternativa es el uso de una variable de control que permita saber si el hilo se encuentra en ejecución o no, por ello, en el ejemplo se utiliza la variable miThread que controla cuando el hilo está en ejecución o parado.

La clase anidada ProcesoRaton es la que se encarga de implementar un objeto receptor de los eventos de ratón, para detectar cuando el usuario pulsa alguno de los botones sobre la zona de influencia del applet.

Suspender y Reanudar Threads

Una vez que se para un hilo de ejecución, ya no se puede rearrancar con el comando start(), debido a que stop() concluirá la ejecución del hilo. Por ello, en ver de parar el hilo, lo que se puede hacer es dormirlo, llamando al método sleep(). El hilo estará suspendido un cierto tiempo y luego reanudará su ejecución cuando el límite fijado se alcance. Pero esto no es útil cuando se necesite que el hilo reanude su ejecución ante la presencia de ciertos eventos. En estos casos, el método suspend() permite que cese la ejecución del hilo y el método resume() permite que un método suspendido reanude su ejecución. En la versión modificada del ejemplo anterior, java1005.java, se modifica el applet para que utilice los métodos suspend() y resume():

El uso de suspend() es crítico en ocasiones, sobre todo cuando el hilo que se va a suspender está utilizando recursos del sistema, porque en el momento de la suspensión los va a bloquear, y esos recursos seguirán bloqueados hasta que no se reanude la ejecución del hilo con resume(). Por ello, deben utilizarse métodos alternativos a estos, por ejemplo, implementando el uso de variables de control que vigiles periódicamente el estado en que se encuentra el hilo actual y obren el consecuencia.

public class java1005 extends Applet implements Runnable {

    ...

class ProcesoRaton extends MouseAdapter {
    boolean suspendido;

    public void mousePressed( MouseEvent evt ) {
    if( suspendido )
            t.resume();
        else
            t.suspend();
        suspendido = !suspendido;
        }
    }
    ...

Para controlar el estado del applet, se ha modificado el funcionamiento del objeto Listener que recibe los eventos del ratón, en donde se ha introducido la variable suspendido. Diferenciar los distintos estados de ejecución del applet es importante porque algunos métodos pueden generar excepciones si se llaman desde un estado erróneo. Por ejemplo, si el applet ha sido arrancado y se detiene con stop(), si se intenta ejecutar el método start(), se generará una excepción IllegalThreadStateException.

Aquí podemos poner de nuevo en cuarentena la idoneidad del uso de estos métodos para el control del estado del hilo de ejecución, tanto por lo comentado del posible bloqueo de recursos vitales del sistema, como porque se puede generar un punto muerto en el sistema si el hilo de ejecución que va a intentar revivir el hilo suspendido necesita del recurso bloqueado. Por ello, es más seguro el uso de una variable de control como suspendido, de tal forma que sea ella quien controle el estado del hilo y utilizar el método notify() para indicar cuando el hilo vuelve a la vida.

Navegador

Home | Anterior | Siguiente | Indice | Correo