next up previous contents
Siguiente: 11 Connected Limited Device Subir: Introducción a Java y Anterior: 9 Gestión de excepciones   Índice General

Subsecciones

10 Programación Multihilo (Multihebra)

1 Hebras en CLDC

Algunas diferencias

2 El hilo principal

Ejemplo de acceso al hilo principal: P44/CurrentThreadDemo.java

class CurrentThreadDemo {
  public static void main(String args[]) {
    Thread t = Thread.currentThread();
    System.out.println("Hilo actual: " + t);
    try {
      for(int n = 5; n > 0; n--) {
        System.out.println(n);
        Thread.sleep(1000);
      }
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo principal");}
  }
}
Salida del programa
Hilo actual: Thread[Thread-0,5]
5
4
3
2
1

3 Creación de un hilo

En Java hay dos formas de crear nuevos hilos:

1 Implementación del interfaz Runnable

Ejemplo: P45/ThreadDemo.java

class NewThread implements Runnable {
  Thread t;
  NewThread() {
    t = new Thread(this, "Hilo hijo");// Crea un nuevo hilo
    System.out.println("Hilo hijo: " + t);
    t.start(); // Comienza el hilo
  }
  public void run() {//Punto de entrada del segundo hilo
    try {
      for(int i = 5; i > 0; i--) {
        System.out.println("Hilo hijo: " + i);
        Thread.sleep(500); }
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo hijo."); }
    System.out.println("Sale del hilo hijo.");
  }
}
class ThreadDemo {
  public static void main(String args[]) {
    new NewThread(); // crea un nuevo hilo
    try {
      for(int i = 5; i > 0; i--) {
        System.out.println("Hilo principal: " + i);
        Thread.sleep(1000);
      }
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo principal.");
    }
    System.out.println("Sale del hilo principal.");
  }
}

Salida del anterior programa

Hilo hijo: Thread{Hilo hijo,5]
Hilo principal:5
Hilo hijo:5
Hilo hijo:4
Hilo principal:4
Hilo hijo:3
Hilo hijo:2
Hilo principal:3
Hilo hijo:1
Sale del hilo hijo.
Hilo principal:2
Hilo principal:1
Sale del hilo principal.

2 Extensión de la clase Thread

Ejemplo: P46/ExtendThread.java

class NewThread extends Thread {
  NewThread() {
    super("Hilo Demo"); // Crea un nuevo hilo
    System.out.println("Hilo hijo: " + this);
    start(); // Comienza el hilo
  }
  public void run() {// Este es el punto de entrada del segundo hilo
    try {
      for(int i = 5; i > 0; i--) {
        System.out.println("Hilo hijo: " + i);
        Thread.sleep(500); }
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo hijo.");
    }
    System.out.println("Sale del hilo hijo.");
  }
}
class ExtendThread {
  public static void main(String args[]) {
    new NewThread(); // crea un nuevo hilo
    try {
      for(int i = 5; i > 0; i--) {
        System.out.println("Hilo principal: " + i);
        Thread.sleep(1000);
      }
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo principal.");
    }
    System.out.println("Sale del hilo principal.");
  }
}

3 Elección de una de las dos opciones

Depende del tipo de clase que se vaya a crear.

4 Creación de múltiples hilos

Un programa en Java puede crear tantos hilos como quiera.

Ejemplo de creación de tres hilos: P47/MultiThreadDemo.java

class NewThread implements Runnable {
  String name; // nombre del hilo
  Thread t;
  NewThread(String threadname) {
    name = threadname;
    t = new Thread(this, name);
    System.out.println("Nuevo hilo: " + t);
    t.start(); // Comienza el hilo
  }
  // Este es el punto de entrada del hilo.
  public void run() {
    try {
      for(int i = 5; i > 0; i--) {
        System.out.println(name + ": " + i);
        Thread.sleep(1000);
      }
    }

    catch (InterruptedException e) {
      System.out.println(name + "Interrupción del hilo hijo" +name);
    }
    System.out.println("Sale del hilo hijo" + name);
  }
}
class MultiThreadDemo {
  public static void main(String args[]) {
    new NewThread("Uno"); // comienzan los hilos
    new NewThread("Dos");
    new NewThread("Tres");
    try {
      // espera un tiempo para que terminen los otros hilos
      Thread.sleep(10000);
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo principal");
    }
    System.out.println("Sale del hilo principal.");
  }
}

Salida del programa

Nuevo hilo: Thread[Uno,5]
Nuevo hilo: Thread[Dos,5]
Nuevo hilo: Thread[Tres,5]
Uno: 5
Dos: 5
Tres: 5
Uno: 4
Dos: 4
Tres: 4
Uno: 3
Dos: 3
Tres: 3
Uno: 2
Dos: 2
Tres: 2
Uno: 1
Dos: 1
Tres: 1
Sale del hilo.Uno
Sale del hilo.Dos
Sale del hilo.Tres
Sale del hilo principal.

5 Utilización de isAlive() y join()

Hay dos formas de determinar si un método ha terminado.

Ejemplo de uso de isAlive() y join(): P48/DemoJoin.java

class NewThread implements Runnable {
  String name; // nombre del hilo
  Thread t;
  NewThread(String threadname) {
    name = threadname;
    t = new Thread(this, name);
    System.out.println("Nuevo hilo: " + t);
    t.start(); // Comienza el hilo
  }
  public void run() {// Este es el punto de entrada del hilo
    try {
      for(int i = 5; i > 0; i--) {
        System.out.println(name + ": " + i);
        Thread.sleep(1000); }
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo"+name); }
    System.out.println("Sale del hilo " + name);
  }
}
class DemoJoin {
  public static void main(String args[]) {
    NewThread ob1 = new NewThread("Uno");
    NewThread ob2 = new NewThread("Dos");
    NewThread ob3 = new NewThread("Tres");
    System.out.println("El hilo Uno está vivo: " + ob1.t.isAlive());
    System.out.println("El hilo Dos está vivo: " + ob2.t.isAlive());
    System.out.println("El hilo Tres está vivo: " + ob3.t.isAlive());
    try {// espera hasta que terminen los otros hilos
      System.out.println("Espera finalización de los otros hilos.");
      ob1.t.join(); ob2.t.join(); ob3.t.join();
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo principal"); }
    System.out.println("El hilo Uno está vivo: " + ob1.t.isAlive());
    System.out.println("El hilo Dos está vivo " + ob2.t.isAlive());
    System.out.println("El hilo Tres está vivo: " + ob3.t.isAlive());
    System.out.println("Sale del hilo principal.");
  }
}

Salida del programa

Nuevo hilo: Thread[Uno,5]
Nuevo hilo: Thread[Dos,5]
Nuevo hilo: Thread[Tres,5]
El hilo Uno está vivo: true
El hilo Dos está vivo: true
El hilo Tres está vivo: true
Espera finalización de los otros hilos.
Uno: 5
Dos: 5
Tres: 5
Uno: 4
Dos: 4
Tres: 4
Uno: 3
Dos: 3
Tres: 3
Uno: 2
Dos: 2
Tres: 2
Uno: 1
Dos: 1
Tres: 1
Sale del hilo Uno
Sale del hilo Dos
Sale del hilo Tres
El hilo Uno está vivo: false
El hilo Dos está vivo false
El hilo Tres está vivo: false
Sale del hilo principal.

6 Prioridades de los hilos

Ejemplo: P49/HiLoPri.java

class clicker implements Runnable {
  int click = 0;
  Thread t;
  private volatile boolean running = true;
  public clicker(int p) {
    t = new Thread(this);
    t.setPriority(p);
  }
  public void run() {
    while (running) {
      click++;
    }
  }
  public void stop() {
    running = false;
  }
  public void start() {
    t.start();
  }
}
class HiLoPri {
  public static void main(String args[]) {
    Thread.currentThread().setPriority(Thread.MAX_PRIORITY);
    clicker hi = new clicker(Thread.NORM_PRIORITY + 2);
    clicker lo = new clicker(Thread.NORM_PRIORITY - 2);
    lo.start();
    hi.start();
    try {
      Thread.sleep(10000);
    } catch (InterruptedException e) {
        System.out.println("Hilo principal interrumpido.");}
    lo.stop();
    hi.stop();
    try {
      hi.t.join();
      lo.t.join();
    } catch (InterruptedException e) {
      System.out.println("InterruptedException capturada");
    }
    System.out.println("Hilo de prioridad baja: " + lo.click); 
    System.out.println("Hilo de prioridad alta: " + hi.click);
  }
}

Salida del programa en linux Redhat 8.0

Hilo de prioridad baja: 9636208
Hilo de prioridad alta: 22480211

7 Sincronización

1 Uso de métodos sincronizados

Ejemplo de programa que no usa sincronización: P50/Synch.java

class Callme {
  void call(String msg) {
    System.out.print("[" + msg);
    try {
      Thread.sleep(1000);
    } catch(InterruptedException e) {
      System.out.println("Interrumpido");
    }
    System.out.println("]");
  }
}
class Caller implements Runnable {
  String msg;
  Callme target;
  Thread t;
  public Caller(Callme targ, String s) {
    target = targ;
    msg = s;
    t = new Thread(this);
    t.start();
  }
  public void run() {
    target.call(msg);
  }
}

class Synch {
  public static void main(String args[]) {
    Callme target = new Callme();
    Caller ob1 = new Caller(target, "Hola");
    Caller ob2 = new Caller(target, "Mundo");
    Caller ob3 = new Caller(target, "Sincronizado");
    try {
      ob1.t.join();
      ob2.t.join();
      ob3.t.join();
    } catch(InterruptedException e) {
      System.out.println("Interrumpido");
    }
  }
}
Salida del anterior programa
[Hola[Mundo[Sincronizado]
]
]

2 Sentencia synchronized

Ejemplo que hace lo mismo de antes: P52/Synch1.java

class Callme {
  void call(String msg) {
    System.out.print("[" + msg);
    try {
      Thread.sleep(1000);
    } catch (InterruptedException e) {
      System.out.println("Interrumpido");
    }
    System.out.println("]");
  }
}
class Caller implements Runnable {
  String msg;
  Callme target;
  Thread t;
  public Caller(Callme targ, String s) {
    target = targ;
    msg = s;
    t = new Thread(this); t.start();
  }
  public void run() {
    synchronized(target) { target.call(msg);}
  }
}
class Synch1 {
  public static void main(String args[]) {
    Callme target = new Callme();
    Caller ob1 = new Caller(target, "Hola");
    Caller ob2 = new Caller(target, "Sincronizado");
    Caller ob3 = new Caller(target, "Mundo");
    try {
      ob1.t.join();
      ob2.t.join();
      ob3.t.join();
    } catch(InterruptedException e) {
      System.out.println("Interrumpido");
    }
  }
}

8 Comunicación entre hilos

Productor/consumidor de un sólo carácter (versión errónea): P53/PC.java

class Q {
  int n;
  synchronized int get() {
    System.out.println("Obtengo: " + n);
    return n;
  }
  synchronized void put(int n) {
    this.n = n;
    System.out.println("Pongo: " + n);
  }
}
class Producer implements Runnable {
  Q q;
  Producer(Q q) {
    this.q = q;
    new Thread(this, "Productor").start();
  }
  public void run() {
    int i = 0;
    while(true) {
      q.put(i++);
    }
  }
}

class Consumer implements Runnable {
  Q q;
  Consumer(Q q) {
    this.q = q;
    new Thread(this, "Consumidor").start();
  }
  public void run() {
    while(true) {
      q.get();
    }
  }
}
class PC {
  public static void main(String args[]) {
    Q q = new Q();
    new Producer(q);
    new Consumer(q);
    System.out.println("Pulsa Control-C para parar.");
  }
}

Salida del programa

Pongo: 1
Obtengo: 1
Obtengo: 1
Obtengo: 1
Obtengo: 1
Obtengo: 1
Pongo: 2
Pongo: 3
Pongo: 4
Pongo: 5
Pongo: 6
Pongo: 7
Obtengo: 7

Solución correcta con wait y notify: P54/PCFixed.java

class Q {
  int n;
  boolean valueSet = false;
  synchronized int get() {
    if(!valueSet)
      try {
        wait();
      } catch(InterruptedException e) {
        System.out.println("InterruptedException capturada");
      }
    System.out.println("Obtengo: " + n);
    valueSet = false;
    notify();
    return n;
  }
  synchronized void put(int n) {
    if(valueSet)
      try {
        wait();
      } catch(InterruptedException e) {
        System.out.println("InterruptedException capturada");
      }
    this.n = n;
    valueSet = true;
    System.out.println("Pongo: " + n);
    notify();
  }
}
class Producer implements Runnable {
  Q q;
  Producer(Q q) {
    this.q = q;
    new Thread(this, "Productor").start();
  }
  public void run() {
    int i = 0;
    while(true) {
      q.put(i++);
    }
  }
}
class Consumer implements Runnable {
  Q q;
  Consumer(Q q) {
    this.q = q;
    new Thread(this, "Consumidor").start();
  }
  public void run() {
    while(true) {
      q.get();
    }
  }
}
class PCFixed {
  public static void main(String args[]) {
    Q q = new Q();
    new Producer(q);
    new Consumer(q);
    System.out.println("Pulsa Control-C para parar.");
  }
}
Salida del programa
Pongo: 1
Obtengo: 1
Pongo: 2
Obtengo: 2
Pongo: 3
Obtengo: 3
Pongo: 4
Obtengo: 4
Pongo: 5
Obtengo: 5
Pongo: 6
Obtengo: 6
Pongo: 7
Obtengo: 7

1 Interbloqueos

Ejemplo de interbloqueo: P55/Deadlock.java

class A {
  synchronized void foo(B b) {
    String name = Thread.currentThread().getName();
    System.out.println(name + " entró en A.foo");
    try {
      Thread.sleep(1000);
    } catch(Exception e) {
      System.out.println("A Interrumpido");
    }
    System.out.println(name + " intentando llamar a B.last()");
    b.last();
  }
  synchronized void last() {
    System.out.println("Dentro de A.last");
  }
}
class B {
  synchronized void bar(A a) {
    String name = Thread.currentThread().getName();
    System.out.println(name + " entró en B.bar");
    try {
      Thread.sleep(1000);
    } catch(Exception e) {
      System.out.println("B Interrumpido");
    }
    System.out.println(name + " intentando llamar a A.last()");
    a.last();
  }
  synchronized void last() {
    System.out.println("Dentro de A.last");
  }
}
class Deadlock implements Runnable {
  A a = new A();
  B b = new B();
  Deadlock() {
    Thread t = new Thread(this, "RacingThread");
    t.start();
    a.foo(b); 
    System.out.println("Regreso al hilo principal");
  }
  public void run() {
    b.bar(a); 
    System.out.println("Regreso al otro hilo");
  }
  public static void main(String args[]) {
    new Deadlock();
  }
}

Salida del programa hasta que queda bloqueado

Thread-0 entró en A.foo
RacingThread entró en B.bar
Thread-0 intentando llamar a B.last()
RacingThread intentando llamar a A.last()

9 Suspender, reanudar y terminar hilos

Ejemplo de uso: P57/SuspendResume.java

class NewThread implements Runnable {
  String name; // nombre del hilo
  Thread t;
  boolean suspendFlag;
  
  NewThread(String threadname) {
    name = threadname;
    t = new Thread(this, name);
    System.out.println("Nuevo hilo: " + t);
    suspendFlag = false;
    t.start(); // Comienza el hilo
  }

  // Este es el punto de entrada del hilo.
  public void run() {
    try {
      for(int i = 15; i > 0; i--) {
        System.out.println(name + ": " + i);
        Thread.sleep(200);
        synchronized(this) {
          while(suspendFlag) {
            wait();
          }
        }
      }
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo" + name);
    }
    System.out.println("Sale del hilo" + name);
  }
  void mysuspend() {
    suspendFlag = true;
  }
  synchronized void myresume() {
    suspendFlag = false;
    notify();
  }
}
class SuspendResume {
  public static void main(String args[]) {
    NewThread ob1 = new NewThread("Uno");
    NewThread ob2 = new NewThread("Dos");
    try {
      Thread.sleep(1000);
      ob1.mysuspend();
      System.out.println("Suspende el hilo Uno");
      Thread.sleep(1000);
      ob1.myresume();
      System.out.println("Reanuda el hilo Uno");
      ob2.mysuspend();
      System.out.println("Suspende el hilo Dos");
      Thread.sleep(1000);
      ob2.myresume();
      System.out.println("Reanuda el hilo Dos");
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo principal");
    }
    // espera hasta que terminen los otros hilos
    try {
      System.out.println("Espera finalización de los otros hilos.");
      ob1.t.join();
      ob2.t.join();
    } catch (InterruptedException e) {
      System.out.println("Interrupción del hilo principal");
    }
    System.out.println("Sale del hilo principal.");
  }
}




next up previous contents
Siguiente: 11 Connected Limited Device Subir: Introducción a Java y Anterior: 9 Gestión de excepciones   Índice General
Andres Cano Utrera 2006-09-23