Diferencia entre revisiones de «Programación de fíos con Java»

De MediaWiki
Ir a la navegación Ir a la búsqueda
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

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.