synchronized
, los métodos Object.wait()
, Object.notify()
y Object.notifyAll()
, y la clase Thread
) están incluidos en CLDC.
Algunas diferencias
ThreadGroup
.
setName()
no existe. El método getName()
existe sólo en CLDC 1.1
resume()
, suspend
y stop()
se han eliminado.
destroy()
, interrupt()
y isInterrupted()
no existen.
dumpStack()
se ha eliminado.
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
t
como argumento de println()
aparece el nombre de la hebra y su prioridad.
setName(String)
en la clase Thread
como en J2SE, lo que hace que sólo podamos asociar un nombre con el constructor Thread(String)
.
run()
:
public abstract void run()
run()
incluimos el código a ejecutar por el
nuevo hilo.
Thread(Runnable objetoHilo,String nombreHilo)
start()
con el objeto
anterior.
synchronized void start()
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.
run()
:
run()
incluimos el código a ejecutar por el
nuevo hilo.
start()
con el objeto
anterior.
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."); } }
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.
isAlive()
. Devuelve true
si el hilo al que se hace referencia está todavía
ejecutándose.
final boolean isAlive() throws InterruptedException
join()
. Este método detiene el hilo
actual hasta que termine el hilo sobre el que se llama join()
. Es usado por tanto para que unos hilos esperen a la finalización de otros.
final void join throws InterruptedException
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.
final void setPriority(int level)
level
puede variar entre MIN_PRIORITY
y MAX_PRIORITY
(1 y 10 en la actualidad). La prioridad por defecto es NORM_PRIORITY
(5 actualmente).
final int getPriority()
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
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] ] ]
call()
del mismo objeto a la vez.
call()
a un sólo hilo en un momento determinado.
class Callme { synchronized void call(String msg) { ...
[Hola] [Mundo] [Sincronizado]
synchronized(objeto){ // sentencias que se sincronizan }
objeto
es el objeto que sincronizamos.
objeto
se ejecutará después de que el hilo actual entre en el monitor de objeto
.
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"); } } }
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
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()
Por ejemplo, un hilo puede usarse para mostrar la hora actual. Si el usuario no desea verla en un momento determinado, entonces tal hilo debería suspenderse.
wait()
y notify()
o notifyAll()
de la clase Object
.
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."); } }