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 130: Línea 130:
  
 
= Sincronización de fíos =
 
= Sincronización de fíos =
 +
Un programa pode lanzar múltiples fíos que colaboren entre sí para a realización dunha tarefa. Deben utilizarse mecanismos de sincronización para evitar problemas que se podan dar...
  
 
= Interrupción de fios =
 
= Interrupción de fios =

Revisión del 05:24 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.

 1 package lanzahilosyesperaqueterminen;
 2 import java.util.Random;
 3 class Hilo implements Runnable {
 4   private nal String nombre;
 5   Hilo(String nombre) {
 6     this.nombre = nombre;
 7   }
 8   @Override
 9   public void run() {
10     System.out.printf(Hola, soy el hilo: %s.\n, this.nombre);
11     Random r = new Random();
12     for (int i = 0; i < 5; i++) {
13       int pausa = 10 + r.nextInt(500 - 10);
14       System.out.printf(Hilo: %s hace pausa de %d ms.\n,
15         this.nombre, pausa);
16       try {
17         Thread.sleep(pausa);
18       } catch (InterruptedException e) {
19         System.out.printf(Hilo %s interrumpido.\n, this.nombre);
20       }
21     }
22     System.out.printf(Hilo %s terminado.\n, this.nombre);
23   }
24 }
 1 public class LanzaHilosYEsperaQueTerminen {
 2   public static void main(String[] args) {
 3     Thread h1 = new Thread(new Hilo(H1));
 4     Thread h2 = new Thread(new Hilo(H2));
 5     h1.start();
 6     h2.start();
 7     try {
 8       h1.join();
 9       h2.join();
10     } catch (InterruptedException e) {
11       System.out.println(Hilo principal interrumpido.);
12     }
13     System.out.println(Hilo principal terminado.);
14   }
15 }

Sincronización de fíos

Un programa pode lanzar múltiples fíos que colaboren entre sí para a realización dunha tarefa. Deben utilizarse mecanismos de sincronización para evitar problemas que se podan dar...

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.