No es un bug, es una característica no documentada

miércoles, 4 de noviembre de 2015

Procesos y servicios. Programación multihilo



  • New (Nuevo). Es el estado cuando se crea un objeto hilo con el operador new. En este estado el hilo aún no se ejecuta; es decir, el programa no ha comenzado la ejecución del código del método run() del hilo.
  • Runnable (Ejecutable). Cuando se invoca al método start(), el hilo pasa a este estado. El sistema operativo tiene que asignar tiempo de CPU al hilo para que se ejecute; por tanto, en este estado el hilo puede estar o no en ejecución.
  • Dead (Muerto). Un hilo muere por varias razones:
    • De muerte natural, porque el método run() finaliza con normalidad.
    • Repentinamente debido a alguna excepción no capturada en el método run().
    • Invocando al método stop(), pero este método está en desuso.



public class HiloEjemploDead extends Thread{
      
       // Propiedades
       private boolean stopHilo = false;
      
       // Métodos
       public void pararHilo(){
             stopHilo = true;
       }
      
       public void run(){
             while(!stopHilo){
                    System.out.println("En el hilo");
             }
       }
      
       // Main
       public static void main(String[] args){
             HiloEjemploDead h = new HiloEjemploDead();
             h.start();
             for (int i = 0; i < 100000; i++); // No hago nada
             h.pararHilo();
       }
}

  • Blocked (Bloqueado). En este estado podría ejecutarse el hilo, pero hay algo que lo evita. Un hilo entra en estado bloquedo cuando ocurre una de las siguientes acciones:
    • Alguien llama al método sleep() del hilo, es decir, se ha puesto a dormir.
    • El hilo está esperando a que se complete una operación de entrada / salida.
    • El hilo llama al método wait(). El hilo no se volverá ejecutable hasta que reciba los mensajes notify() o notifyAll().
    • El hilo intenta bloquear un objeto que está actualmente bloqueado por otro hilo.
    • Alguien llama al método suspend() del hilo. No se volverá a ejecutar de nuevo hasta que recia el mensaje resume().

Gestión de un hilo

Crear y arrancar

Para crear un hilo extendemos la clase Thread o implementamos la interfaz Runnable.

// Se crea un hilo
MiHilo h = new MiHilo(“Hilo 1”, 200);

// Se arranca el hilo
h.start(); // Si la clase extienda a Thread
new Thread(h).start(); // Si implementa a Runnable

Lo que hace el método start() es llamar al método run() del hilo que es donde se colocan las acciones que queremos que haga el hilo, cuando finalice el método finalizará también el hilo.

Suspensión

El método suspend() permite detener la actividad del hilo durante un intervalo de tiempo indeterminado.

Para volver a activar el hilo se necesita invocar al método resume().

El método suspend() es un método obsoleto y tiende a no utilizarse porque puede producir situaciones de interbloqueos. Por ejemplo, si un hilo está bloqueando un recurso y este hilo se suspende, puede dar lugar a que otros hilos que esperaban el recurso queden “congelados” ya que el hilo suspendido mantiene los recursos bloqueados. Igualmente el método resume() también está en desuso.

Para suspender de forma segura el hilo se debe introducir dentro de este una variable, por ejemplo suspender, y comprobar su valor dentro del método run().

class MyThread extends Thread{
      
       // Propiedades
       private SuspendRequestor suspender = new SuspendRequestor();
      
       // Métodos
       public void requestSuspend(){
             suspender.set(true);
       }
      
       public void requestResume(){
             suspender.set(false);
       }
      
       public void run(){
             try{
                    while(/* haya trabajo por hacer */ true){
                           suspender.waitForResume(); // Realizar el trabajo
                    }
             }
             catch(InterruptedException exception){
                    e.printStackTrace();
             }
       }
}

class SuspendRequestor{
      
       // Propiedades
       private boolean suspendRequested;
      
       // Métodos
       public synchronized void set(boolean b){
             suspendRequested = b;
             notifyAll();
       }
      
       public synchronized void waitForResume() throws InterruptedException{
             while(suspendRequested)
                    wait();
       }
}

Parada

Los métodos stop(), suspend(), resume() y destroy() han sido abolidos en Java 2 para reducir la posibilidad de interbloqueo.

El método isAlive() devuelve true si el hilo está vivo, es decir, ha llamado a su método run() y aún no ha terminado su ejecución o no ha sido detenido con stop(); en caso contrario devuelve false.

El método interrupt() envía una petición de interrupción a un hilo. Si el hilo se encuentra bloqueado por una llamada a sleep() o wait() se lanza una excepción InterruptedException.

El método isInterrupted() devuelve true si el hilo ha sido interrumpid, en caso contrario devuelve false.

public class HiloEjemploInterrupt extends Thread{
      
       public void run(){
             try{
                    while (!isInterrupted()){
                           System.out.println("En el hilo");
                           Thread.sleep(10);
                    }
             }
             catch(InterruptedException e){
                    System.out.println("Ha ocurrido una excepción");
             }
             System.out.println("Fin de hilo");
       }
      
       public void interrumpir(){
             interrupt();
       }
      
       public static void main(String[] args){
             HiloEjemploInterrupt h = new HiloEjemploInterrupt();
             h.start();
             try{
                    sleep(20);
             }
             catch(InterruptedException e){
                    h.interrumpir();
             }
       }
}

El método join() provoca que el hilo que hace la llamada espere la finalización de otros hilos.

public class HiloJoin extends Thread{
      
       private int n;
      
       public HiloJoin(String nom, int n){
             super(nom);
             this.n = n;
       }
      
       public void run(){
             for (int i = 1; i <= n; i++){
                    System.out.println(getName() + ":" + i);
                    try{
                           sleep(1000);
                    }
                    catch(InterruptedException ignore){}
             }
             System.out.println("Fin bucle " + getName());
       }
}

public class EjemploJoin{
       public static void main(String[] args){
             HiloJoin h1 = new HiloJoin("Hilo 1", 2);
             HiloJoin h2 = new HiloJoin("Hilo 2", 5);
             HiloJoin h3 = new HiloJoin("Hilo 3", 7);
            
             h1.start();
             h2.start();
             h3.start();
            
             try{
                    h1.join();
                    h2.join();
                    h3.join();
             }
             catch(InterruptedException e){}
            
             System.out.println("Final del programa");
       }

}

0 comentarios:

Publicar un comentario