Diferencia entre revisiones de «Programación de fíos con Java»
Línea 36: | Línea 36: | ||
= A clase Thread = | = A clase Thread = | ||
+ | A clase Thread proporciona a funcionalidad esencial para a creación, execución e xestión de fíos. | ||
+ | |||
+ | O crado resume os principales métodos desta clase. | ||
+ | {| class="wikitable" | ||
+ | !Método | ||
+ | !Funcionalidade | ||
+ | |- | ||
+ | |void run() | ||
+ | |Execútase cando se lanza o fío. É o punto de entrada do fío, coma o método main() é o punto de entrada do proceso. | ||
+ | |- | ||
+ | |void start() | ||
+ | |Lanza o fío. A JVM crea o fío e executa o seu método run() | ||
+ | |- | ||
+ | |static void sleep(long ms) <br/> static void sleep(long ms, long ns) | ||
+ | |Detén a execución do fío actualmente en execución durante un tempo, que se pode indicar en microsegundos ou nunha combinación de microsegundos e nanosegundos. | ||
+ | |- | ||
+ | |void join() <br/> void join(long ms) <br/> void join(long ms, long ns) | ||
+ | |Agarda a que termine o fío. Pódese indicar un tempo máximo de espera, ben en milisegundos, bien nunha combinación de milisegundos e nanosegundos. | ||
+ | |- | ||
+ | |public void interrupt() <br/> public boolean isInterrupted() <br/> public static boolean interrupted() | ||
+ | | O primero interrumpe a execución dun fío; o segundo verifica se foi interrumpido un fío; o terceiro (estático) verifica se foi interrumpida a execución do fío actual, e borra o estado de interrupción, de xeito que unha chamada posterior devolvería false , a menos que se volva a interrumpir. | ||
+ | |- | ||
+ | |boolean isAlive() | ||
+ | |Comproba se o fío está vivo. Un fío está vivo cando foi iniciado e non rematou a súa execución. | ||
+ | |- | ||
+ | |int getPriority() <br/> void setPriority(int nuevaPrior) | ||
+ | |Podese asignar unha prioridade a un fío, e se pode obtener a prioridade dun fío. | ||
+ | |- | ||
+ | |static Thread currentThread() | ||
+ | |Devolve un obxecto de clase Thread correspondente ó fío en execución actualmente. | ||
+ | |- | ||
+ | |long getId() | ||
+ | |Devolve o identificador do fío | ||
+ | |- | ||
+ | |String getName() <br/> void setName(String nombre) | ||
+ | |Pódese asignar un nome a un fillo, e se pode recuperar o nome do fío. | ||
+ | |- | ||
+ | |Thread.State getState() | ||
+ | |Devolve o estado do fío. | ||
+ | |- | ||
+ | |boolean isDaemon() <br/> void setDaemon(boolean on) | ||
+ | | Un fío pode ser de tipo ''daemon''. A distinción é importante porque a JVM termina a súa ejecución cando non queda ningún fío activo ou cando só quedan fíos de tipo daemon. | ||
+ | |} | ||
+ | |||
+ | O seguinte programa lanza dous fíos. Cada un deles fai pausas de duración aleatoria de ente 10 e 500 ms, utilizando o método sleep da clase Thread. | ||
+ | |||
+ | O fio principal utiliza o método join para esperar a que terminen os dous fillos lanzados, polo que seepre terminará o último. Os dous métodos anteriores pausan a execución do fío, e durante ese periodo de tempo se podería interrumpir. Se esto sucede, lanzaríase unha InterruptedException, que se captura para amosar unha mensaxe. | ||
+ | |||
+ | package lanzahilosyesperaqueterminen; | ||
+ | import java.util.Random; | ||
+ | class Hilo implements Runnable { | ||
+ | private nal String nombre; | ||
+ | Hilo(String nombre) { | ||
+ | this.nombre = nombre; | ||
+ | } | ||
+ | @Override | ||
+ | public void run() { | ||
+ | System.out.printf(“Hola, soy el hilo: %s.\n”, this.nombre); | ||
+ | Random r = new Random(); | ||
+ | for (int i = 0; i < 5; i++) { | ||
+ | int pausa = 10 + r.nextInt(500 - 10); | ||
+ | System.out.printf(“Hilo: %s hace pausa de %d ms.\n”, | ||
+ | this.nombre, pausa); | ||
+ | try { | ||
+ | Thread.sleep(pausa); | ||
+ | } catch (InterruptedException e) { | ||
+ | System.out.printf(“Hilo %s interrumpido.\n”, this.nombre); | ||
+ | } | ||
+ | } | ||
+ | System.out.printf(“Hilo %s terminado.\n”, this.nombre); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | public class LanzaHilosYEsperaQueTerminen { | ||
+ | public static void main(String[] args) { | ||
+ | Thread h1 = new Thread(new Hilo(“H1”)); | ||
+ | Thread h2 = new Thread(new Hilo(“H2”)); | ||
+ | h1.start(); | ||
+ | h2.start(); | ||
+ | try { | ||
+ | h1.join(); | ||
+ | h2.join(); | ||
+ | } catch (InterruptedException e) { | ||
+ | System.out.println(“Hilo principal interrumpido.”); | ||
+ | } | ||
+ | System.out.println(“Hilo principal terminado.”); | ||
+ | } | ||
+ | } | ||
= Sincronización de fíos = | = Sincronización de fíos = |
Revisión del 05:22 13 sep 2021
Sumario
Introducción
Comezaremos aprendendo a crear e lanzar novos fios (threads) dun proceso con Java, e facer que diferentes fíos dun mesmo proceso colaboren e se coordinen entre sí para facer unha tarefa. No resto deste capítulo veranse os mecanismos e as clases que proporciona a linguaxe Java para a programación de fíos e a súa sincronización.
Creación de fíos en Java
Para executar un programa de Java, iníciase a máquina virtual de Java (JVM) cun único fío que executa o método main() dunha clase. Pero desde este fío podense crear máis fíos, a partir de calquera clase que implemente a interfaz Runnable. Nesta clase hai que programar o método run(), que se executa cando se lanza o fío.
Un fío xestionase con un obxecto da clase Thread. Despois de crealo podese lanzar co seu método start() . Con esto crease o fío e se executa o método start() .
O seguiente programa de exemplo lanza dous fios dun proceso. A funcionalidad dos fíos implementase nunha clase Fío, que implementa a interfaz Runnable. O lanzar o fío execútase o método run() , que escribe un identificador que se pasa no constructor.
1 package lanzahilos; 2 class Fio implements Runnable { 3 private final String nome; 4 Fio(String nome) { 5 this.nome = nome; 6 } 7 @Override 8 public void run() { 9 System.out.printf(“Ola, son o fío: %s.\n”, this.nome); 10 System.out.printf(“Fío %s terminado.\n”, this.nome); 11 } 12 } 13 public class LanzaFios { 14 public static void main(String[] args) { 15 Thread h1 = new Thread(new Fio(“H1”)); 16 Thread h2 = new Thread(new Fio(“H2”)); 17 h1.start(); 18 h2.start(); 19 System.out.println(“Fio principal terminado.”); 20 } 21 }
Se se executa o programa varias veces, pódese ver que en cada execución as instruccións dos tres fíos (o principal e os dous creados por él) intercalan a súa execución de diversas maneiras, e empiezan e rematan a súa execución en distintos órdenes.
A clase Thread
A clase Thread proporciona a funcionalidad esencial para a creación, execución e xestión de fíos.
O crado resume os principales métodos desta clase.
Método | Funcionalidade |
---|---|
void run() | Execútase cando se lanza o fío. É o punto de entrada do fío, coma o método main() é o punto de entrada do proceso. |
void start() | Lanza o fío. A JVM crea o fío e executa o seu método run() |
static void sleep(long ms) static void sleep(long ms, long ns) |
Detén a execución do fío actualmente en execución durante un tempo, que se pode indicar en microsegundos ou nunha combinación de microsegundos e nanosegundos. |
void join() void join(long ms) void join(long ms, long ns) |
Agarda a que termine o fío. Pódese indicar un tempo máximo de espera, ben en milisegundos, bien nunha combinación de milisegundos e nanosegundos. |
public void interrupt() public boolean isInterrupted() public static boolean interrupted() |
O primero interrumpe a execución dun fío; o segundo verifica se foi interrumpido un fío; o terceiro (estático) verifica se foi interrumpida a execución do fío actual, e borra o estado de interrupción, de xeito que unha chamada posterior devolvería false , a menos que se volva a interrumpir. |
boolean isAlive() | Comproba se o fío está vivo. Un fío está vivo cando foi iniciado e non rematou a súa execución. |
int getPriority() void setPriority(int nuevaPrior) |
Podese asignar unha prioridade a un fío, e se pode obtener a prioridade dun fío. |
static Thread currentThread() | Devolve un obxecto de clase Thread correspondente ó fío en execución actualmente. |
long getId() | Devolve o identificador do fío |
String getName() void setName(String nombre) |
Pódese asignar un nome a un fillo, e se pode recuperar o nome do fío. |
Thread.State getState() | Devolve o estado do fío. |
boolean isDaemon() void setDaemon(boolean on) |
Un fío pode ser de tipo daemon. A distinción é importante porque a JVM termina a súa ejecución cando non queda ningún fío activo ou cando só quedan fíos de tipo daemon. |
O seguinte programa lanza dous fíos. Cada un deles fai pausas de duración aleatoria de ente 10 e 500 ms, utilizando o método sleep da clase Thread.
O fio principal utiliza o método join para esperar a que terminen os dous fillos lanzados, polo que seepre terminará o último. Os dous métodos anteriores pausan a execución do fío, e durante ese periodo de tempo se podería interrumpir. Se esto sucede, lanzaríase unha InterruptedException, que se captura para amosar unha mensaxe.
package lanzahilosyesperaqueterminen; import java.util.Random; class Hilo implements Runnable {
private nal String nombre; Hilo(String nombre) { this.nombre = nombre; } @Override public void run() { System.out.printf(“Hola, soy el hilo: %s.\n”, this.nombre); Random r = new Random(); for (int i = 0; i < 5; i++) { int pausa = 10 + r.nextInt(500 - 10); System.out.printf(“Hilo: %s hace pausa de %d ms.\n”, this.nombre, pausa); try { Thread.sleep(pausa); } catch (InterruptedException e) { System.out.printf(“Hilo %s interrumpido.\n”, this.nombre); } } System.out.printf(“Hilo %s terminado.\n”, this.nombre); }
}
public class LanzaHilosYEsperaQueTerminen {
public static void main(String[] args) { Thread h1 = new Thread(new Hilo(“H1”)); Thread h2 = new Thread(new Hilo(“H2”)); h1.start(); h2.start(); try { h1.join(); h2.join(); } catch (InterruptedException e) { System.out.println(“Hilo principal interrumpido.”); } System.out.println(“Hilo principal terminado.”); }
}
Sincronización de fíos
Interrupción de fios
Prioridades
Depuración (debugging) de aplicaciones multifío
Mecanismos de alto nivel para concurrencia
Resumo
- Os fios en Java represéntanse mediante obxectos da clase Thread.
- Podense crear fíos a partir de obxectos de calquera clase que implemente a interfaz Runnable. A propia clase Thread implementa esta interfaz.
- O proceso que realiza un fío impleméntase no método run . Iníciase a execución dun fío co método start, que a súa vez executa o método run.
- O método join suspende a execución do fío actual á espera de que termine a execución do fío para o que se invoca.
- A palabra clave synchronized fai que un fío so poida executar un bloque de código tras obter o bloqueo dun obxecto de bloqueo, que se libera unha vez executado o bloque.
- Se o obxecto de bloqueo está bloqueado, suspéndese a execución do fío á espera de conseguir o bloqueo do obxecto de bloqueo. Deste xeito conseguese que o bloque de código se execute en exclusión mutua con outros fíos.
- Pódese aplicar:
- para métodos non estáticos (nese caso, o obxecto de bloqueo é o obxecto para o que se executa o método, this )
- para métodos estáticos (nese caso, o obxecto de bloqueo é a clase a que pertence ó método)
- para bloques de código cualesquiera (nese caso, especifícase explícitamente o obxecto de bloqueo).
- O interbloqueo ou deadlock prodúce cuando dous ou máis fíos manteñen bloqueados un conxunto de obxectos de bloqueo, e a súa execución está suspendida á espera de conseguir bloquear cada un un novo obxecto de bloqueo, que non poden bloquear porque xa están bloqueados por outros fíos.
- Podese evitar o interbloqueo establecendo unha orde para os obxectos de bloqueo, e todos os hilos se bloquean segundo este orde.
- Si, una vez conseguido el bloqueo sobre un objeto de bloqueo obj en un bloque de código synchronized(obj) , un hilo comprueba que no se dan las condiciones necesarias para continuar con su ejecución, puede ejecutar wait() sobre el objeto de bloqueo con obj.wait() . Con ello, su ejecución queda suspendida, y en espera no activa, mientras que otro objeto no ejecute obj.notifyAll() u obj.notify() . notifyAll() reanuda todos los procesos a la espera en el objeto de bloqueo, mientras que notify() reanuda solo uno de ellos.
- Podense incluir nunha clase os mecanismos de sincronización apropiados para permitir o uso dos seus obxectos por distintos fíos de xeito concurrente. Desa maneira, non é preciso implementar neses fíos ningún mecanismo de sincronización para controlar o acceso concurrente ós objetos da clase. Se dice entonces que la clase es thread-safe o segura para su uso concurrente por parte de varios hilos. O paquete java.util.concurrent inclúe una variedad de clases thread-safe.
- En xeral, deben programarse os fios de xeito que se permita a súa interrupción por parte doutros fíos. Para iso deben xestionar de maneira apropiada a excepción InterruptedException, comprobar cada certo tiempo se foron interrumpidos con isInterrupted() ou interrupted() , e realizar as accións oportunas no seu caso.
- Os entornos de desarrollo adoitan permitir a depuración de aplicacións multifío e proporcionar ferramentas para a detección de interbloqueos.
- o paquete java.util.concurrent proporciona mecanismos de alto nivel para concurrencia.