Programación Avanzada.
Centro Superior de Informática.
Universidad de La Laguna.


Implementación de Flujos de Control Múltiples (Threads).

En ejercicios anteriores se han desarrollado aplicaciones y applets que dibujan un reloj y muestran la fecha y la hora actuales. El reloj no camina después de mostrar la hora. La cuestión que nos plantearemos en está práctica es: ¿Cómo podemos conseguir que el reloj muestre la hora actualizada cada segundo?. La clave de la respuesta está en volver a pintar (repaint) el reloj con la hora actualizada. Por lo tanto, se podría intentar sobreescribir el método start() del applet de la siguiente manera:
public void start() {
  while (true) {
    repaint();
    try {
      Thread.sleep(1000);
    }
    catch( InterruptedException ex) {
    }
  }
}
El método start() se llama cuando el applet empieza a ejecutarse. El bucle infinito dibuja un reloj cada 1.000 milisegundos.  Por lo tanto, parece que se debería refrescar la pantalla cada segundo con un nuevo reloj, sin embargo, si se ejecuta el programa sobre un un navegador, se nos cuelga. El problema reside en que mientras se ejecute el bucle infinito, el navegador no puedrá atender a ningún otro evento que pueda estar sucediendo. Por lo tanto, el metódo paint() no es llamado nunca. Así pues, la solución a nuestro problema consiste en mover el bucle while a otro thread que se ha de ejecutar en paralelo con el thread del applet que ejecuta el método paint().

Para crear un Thread para un applet, es necesario implementar la interface Runnable. Para ello podemos seguir los siguientes pasos generales:

  1. Añadir en la declaración de la clase implements Runnable:
    public class TestApplet extends Applet implements Runnable

     
  1. Declarar un objeto Thread en el applet TestApplet. Por ejemplo, las siguientes sentencias declaran una instancia de la clase Thread, timer, con valor inicial nulo:
    private Thread timer = null;

    Por defecto, el valor inicial es nulo, así que la asignación al valor "null" no es necesaria.
     

  1. Crear un thread (con el operador new) en el método init() del applet  y ponerla en marcha (llamando a su método start()):

  2.  
      public void init(){
        timer = new Thread(this);  //crear el thread
        timer.start();  //poner en marcha el thread
      }
       
    El argumento "this" en el constructor del thread es indispensable, puesto que especifica que el método run() de TestApplet es el que se debe llamar cuando se ejecute el thread.
    Nótese que el método start()en timer.start() es diferente del método start() del applet. El método start() del thread provoca que el método run() se ejecute, mientras que el método start() del applet es invocado por el navegador cuando se abre por primera vez la página que contine al applet o cuando se activa por sucesivas visitas.
     
  3. Reanudar el thread en el método start() del applet mediante una llamada a un método resume():

  4.  
      public void start(){
        resume();
      }
       
    El método resume() reanuda al thread (lo pone en marcha otra vez) si había sido suspendido.  Este método es ignorado if el thread no había sido suspendido. Puesto que el método resume() de la clase Thread ha sido desautorizado (deprected), es necesario que el usuario implemente el suyo propio en su programa.
     
  5. Implementar los métodos resume() y suspend()como sigue:

  6.  
      public synchroized void resume(){
        if (suspended){
          suspended = false;
          notify();
        }
      }

      public synchronized void suspend(){
        suspended = true;
      }
       

    La variable suspended se debe declarar como una variable miembro de la clase, y en ella se indica el estado del thread. La palabra reservada "synchronized" aseguran que los métodos resume() y suspend() se ejecutan en serie (uno después que el otro) para así evitar las condiciones de carrera (race conditions) que podrían provocar un resultado inconsistente de los valores de la variable suspended.
     
  7. Escribir el código que se quiere que ejecute el thread en el método run():

  8.  
      public void run(){
        while (true){
           repaint();
           try{
             timer.sleep(1000);
             synchronized(this) {
               while(suspended) wait();
              }
           }
           catch (InterruptedException ex) {
           }
        }
      }
       
    El método run() es invocado por el sistema de ejecución de Java cuando el applet empieza (start).  El bucle while repite la llamada al método repaint() cada segundo si el thread no esta suspendido. Si la variable suspended está a true, el método wait() provoca que el thread se suspenda y espere por la notificación que le llegará mediante la llamada al método notify() en el método resume(). El método repaint() se ejecuta sobre el thread por defecto del sistema, el cual está separado del thread sobre el que se ejecuta el bucle while. La palabra reservada "synchronized" elimina los conflictos potenciales que podría causar el que el thread suspendido perdiera una notificación y permaneciera suspendido.
     
  9. Redefinir el método stop() de forma que suspenda el thread que se está ejecutando:

  10.  
      public void stop() {
         suspend();
      }
       
    Este código suspende el thread de manera que no consume tiempo de CPU mientras la página Web que contiene al applet esté inactiva.
     
  11. Redefinir el método destroy() para que mate al thread:

  12.  
      public void destroy () {
        timer = null;
      }
       
    Este código libera todos los recursos asociados con el thread cuando se sale del navegador.
Aunque este ejercicio muestre como implementar la interface Runnable en un applet, dicha interface se puede implementar en cualquier clase.

Bibliografía.

[1] Y. Daniel Liang. "Introduction to Java Programming". QueE&T. 1999
[2] S. Davis. "Aprenda Java ya". McGraw-Hill. Microsoft Press. 1996.
[3] J. Gosling,  H. McGilton. "The Java Language Enviroment". A White Paper. Sun Microsystems. Java Soft. 1996.
[4] D. Flanagan. "Java en pocs Palabras". McGrawHill-O'Reilyy. 1998.
[5] J.F. Macary, C. Nicolas. "Programación Java". Eyrolles.