Title: Tema 12: Programaci
1Tema 12 Programación multihilo
2Índice
- 1. Modelo de hilo en Java.
- 2. El hilo principal.
- 3. Creación de un hilo.
- 4. Creación de múltiples hilos. Prioridades.
- 5. Comunicación entre hilos. Sincronización.
- 6. Modelado UML para la programación multihilo.
Clases activas.
3Introducción
- Un programa multihilo contiene dos o más partes
que pueden ejecutarse de forma concurrente. - Cada parte de ese programa se llama hilo (Thread)
y cada hilo establece un camino de ejecución
independiente. - La concurrencia reúne varios hilos de ejecución.
- Forma especializada de multitarea (multitasking).
- basada en procesos
- basada en hilos
4Multitarea basada en Procesos
- Proceso es un programa que se está ejecutando.
- Multitarea basada en procesos se puede decir que
es la característica que le permite a la
computadora ejecutar dos o más programas
concurrentemente. - Un programa es la unidad de código más pequeña
que el planificador puede seleccionar. - Los procesos son tareas pesadas que necesitan su
propio espacio de direccionamiento. La
comunicación entre procesos es más cara y
limitada. - Es costoso el cambio de contexto de un proceso a
otro.
5Multitarea basada en hilos
- El hilo es la unidad de código más pequeña que se
puede seleccionar. - La multitarea basada en hilos requiere menos
sobrecarga que la multitarea basada en procesos. - Los hilos son más ligeros, ya que comparten el
mismo espacio de direcciones y comparten
cooperativamente el mismo proceso pesado. - La comunicación entre hilos es ligera y el cambio
de contexto de un hilo al siguiente es menos
costoso.
6El modelo de hilo en Java
- Java utiliza hilos para permitir que el entorno
en su globalidad sea asíncrono.
7Estados de los hilos
- Un hilo puede estar ejecutándose.
- Puede estar preparado para ejecutarse tan pronto
como disponga de tiempo de CPU. - Si se está ejecutando puede suspenderse, lo que
equivale a detener temporalmente su actividad. - El hilo suspendido puede reanudarse permitiendo
que continúe su tarea allí donde la dejó. - Un hilo puede estar bloqueado cuando espera un
recurso. - Un hilo puede detenerse, finalizando su ejecución
de manera inmediata. Una vez detenido, un hilo no
puede reanudarse.
8El ciclo de vida de un Thread
En ejecución
yield
start
Ejecución
Suspendido
Nuevo Hilo
El método run termina
Detenido
9Prioridades de los hilos
- El intérprete de Java utiliza prioridades para
determinar cómo debe tratar cada hilo con
respecto a los demás. - La prioridad de un hilo es un valor entero que
asigna un orden de ejecución cuando los hilos
estén preparados para ejecutarse o ejecutándose - La prioridad de un hilo se utiliza para decidir
cuándo se pasa a ejecutar otro hilo. - Esto es lo que se conoce como cambio de contexto.
10Reglas para el cambio de contexto
- Un hilo puede ceder voluntariamente el control.
- Esto se hace por abandono explícito, al quedarse
dormido o al bloquearse en espera de una E/S
pendiente. En este caso, se examinan todos los
hilos restantes y se selecciona para su
asignación a la CPU aquél que, estando listo para
su ejecución, tenga la prioridad más alta. - Un hilo puede ser desalojado por otro con
prioridad más alta. - En este caso, un hilo de baja prioridad que no
libera la CPU es desalojado por otro de mayor
prioridad con independencia de lo que estuviese
haciendo en ese instante.
11Sincronización (I)
- Los hilos permiten y potencian el comportamiento
asíncrono de los programas, - forma de forzar el sincronismo donde sea
necesario - Haciendo que coincidan en el tiempo dos o más
hilos de ejecución - Java implementa una versión de modelo clásico de
sincronización entre procesos, llamado monitor. - El monitor es un mecanismo de control que fue
definido en primer lugar por C.A.R. Hoare y que
puede entenderse como una pequeña caja en la que
sólo cabe un hilo. - Una vez que un hilo entra en el monitor, los
demás deben esperar a que éste salga. - Los monitores se utilizan para proteger un bien
compartido y evitar que sea manipulado por más de
un hilo simultáneamente.
12Sincronización (II)
- Cada objeto tiene su propio monitor implícito en
el que entra automáticamente cuando se llama a
uno de los métodos sincronizados del objeto. - Una vez que un hilo está dentro de un método
sincronizado, ningún otro hilo puede llamar a
otro método sincronizado del mismo objeto.
13El hilo principal
- class HiloActual
- public static void main (String args)
- Thread t Thread.currentThread()
- System.out.println("Hilo actual " t)
-
- //cambia el nombre del hilo
- t.setName("Mi hilo")
- System.out.println("después del cambio de
nombre " t) -
- try
- for (int n 5 ngt0 n--)
- System.out.println(n)
- Thread.sleep(1000)
-
- catch (InterruptedException e)
- System.out.println("Interrupcion del hilo
principal") -
-
-
Hilo actual Threadmain,5,main después del
cambio de nombre ThreadMi hilo,5,main 5 4 3 2 1
14Creación de un hilo
- Dos opciones
- Implementando la interfaz Runnable.
- Extendiendo la clase Thread.
- La clase Thread define varios métodos que pueden
sobrescribir las clases derivadas. - El único que tiene que ser sobrescrito es run().
- Este método es exactamente el mismo que es
necesario para implementar la interfaz Runnable. - Implementar la interfaz permite utilizar herencia
de cualquier otra clase diferente.
15Implementando la interfaz Runnable
- Si un objeto implementa la interfaz Runnable se
puede usar para crear un hilo. - El comienzo del hilo (con start()) provoca que el
método run() del hilo se pueda invocar de forma
separada. - Thread(Runnable objetoHilo, String nombreHilo)
- synchronized void start()
- public abstract void run()
16Ejemplo con Runnable (I)
- //Crea un segundo hilo.
- class NuevoHilo implements Runnable
- Thread t
-
- NuevoHilo() //Crea un nuevo hilo
- t new Thread(this,"Hilo hijo")
- System.out.println("Hilo hijo "t)
- t.start() //comienza el hilo
-
-
- //Este es el punto de entrada del segundo hilo
- public void run()
- try
- for (int i 5 i gt 0 i--)
- System.out.println("Hilo hijo " i)
- Thread.sleep(500)
-
- catch(InterruptedException e)
- System.out.println("Interrupcion de hilo
hijo")
17Ejemplo con Runnable (II)
- class Hilos0
- public static void main(String args)
- new NuevoHilo() //crea un nuevo hilo
-
- try
- for (int i 5 igt0 i--)
- System.out.println("Hilo Principal " i)
- Thread.sleep(1000)
-
- catch(InterruptedException e)
- System.out.println("Interrupcion del hilo
principal") -
- System.out.println("Sale del hilo principal.")
-
-
-
18Extendiendo la clase Thread
- Crea una nueva clase que herede de la clase
Thread y después crear una instancia de esa
clase. - Esta nueva clase debe sobreescribir el método
run(), que es el punto de entrada del nuevo hilo. - También debe llamar al método start() para que
comience la ejecución del nuevo hilo.
19Ejemplo con Thread (I)
- //Crea un hilo extendiendo la clase Thread.
- class NuevoHilo extends Thread
-
- NuevoHilo() //Crea un nuevo hilo
- super("Hilo demo")
- System.out.println("Hilo hijo "this)
- start() //comienza el hilo
-
- //Este es el punto de entrada del segundo hilo
- public void run()
- try
- for (int i 5 i gt 0 i--)
- System.out.println("Hilo hijo " i)
- Thread.sleep(500)
-
- catch(InterruptedException e)
- System.out.println("Interrupcion de hilo
hijo") -
- System.out.println("Sale del hilo hijo")
20Ejemplo con Thread (II)
- class Hilos1
- public static void main(String args)
- new NuevoHilo()//crea un nuevo hilo
- try
- for (int i 5 igt0 i--)
- System.out.println("Hilo Principal " i)
- Thread.sleep(1000)
-
- catch(InterruptedException e)
- System.out.println("Interrupcion del hilo
principal") -
- System.out.println("Sale del hilo principal.")
-
21Creación de múltiples hilos
- Hasta ahora sólo se han utilizado dos hilos
- el hilo principal
- y un hilo hijo.
- Se pueden generar tantos hilos como necesiten.
22Ejemplo, creación de varios hilos (I)
- //Creación de múltiples hilos
- class NuevoHilo implements Runnable
- String nombre
- Thread t
-
- NuevoHilo(String NombreHilo) //Crea un nuevo
hilo - nombre NombreHilo
- t new Thread(this, nombre)
- 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 gt 0 i--)
- System.out.println(nombre " " i)
- Thread.sleep(1000)
-
- catch(InterruptedException e)
23Ejemplo creación de varios hilos (II)
C\jdk1.2.2\bingtjava Hilos2 Nuevo Hilo
ThreadUno,5,main Nuevo Hilo ThreadDos,5,main
Nuevo Hilo ThreadTres,5,main 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.
- class Hilos2
- public static void main(String args)
- new NuevoHilo("Uno")
- new NuevoHilo("Dos")
- new NuevoHilo("Tres")
- try
- Thread.sleep(10000)
- catch(InterruptedException e)
- System.out.println(
- "Interrupcion del hilo principal")
-
- System.out.println(
- "Sale del hilo principal.")
-
-
24Comunicación entre hilos
- Una forma de determinar si un hilo ha terminado
de ejecutarse es llamando al método de la clase
Thread isAlive() - final boolean isAlive() throws InterruptedExceptio
n - Devuelve true si el hilo al que se hace
referencia está todavía ejecutándose. - Devuelve false en caso contrario.
- El método join() se utiliza para esperar la
finalización de un hilo. - final void join() throws InterruptedException
- Este método espera hasta que finalice el hilo
sobre el que se llama. - Su nombre surge de la idea de que el hilo
llamante espera hasta que el hilo especificado se
reúne con él. - Hay otras formas de join() que permiten
especificar el tiempo máximo que se quiere
esperar la finalización de un hilo. - final void join(long millis) throws
InterruptedException - final void join(long millis, int nanos) throws
InterruptedException
25Ejemplo de Comunicación entre hilos (I)
- //Uso del método join() para esperar la
finalización de hilos - class NuevoHilo implements Runnable
- String nombre
- Thread t
-
- NuevoHilo(String NombreHilo) //Crea un nuevo
hilo - nombre NombreHilo
- t new Thread(this, nombre)
- 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 gt 0 i--)
- System.out.println(nombre " " i)
- Thread.sleep(1000)
-
- catch(InterruptedException e)
26Ejemplo de Comunicación entre hilos (II)
- class Hilos3
- public static void main(String args)
- NuevoHilo ob1 new NuevoHilo("Uno")
- NuevoHilo ob2 new NuevoHilo("Dos")
- NuevoHilo ob3 new NuevoHilo("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()) - //espera a que terminen los otros hilos
- try
- System.out.println("Espera finalización de
otros hilos ") - ob1.t.join()
- ob2.t.join()
- ob3.t.join()
- catch(InterruptedException e)
- System.out.println("Interrupcion 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())
La salida de este programa es la
siguiente C\jdk1.2.2\bingtjava Hilos3 Nuevo
Hilo ThreadUno,5,main Nuevo Hilo
ThreadDos,5,main Nuevo Hilo ThreadTres,5,main
El hilo Uno estß vivo true El hilo Dos estß
vivo true El hilo Tres estß vivo true Espera
finalizaci_n de otros hilos 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
27Suspensión y Reanudación de un hilo
- Son dos métodos marcados como Deprecated
- final void resume()
- final void suspend()
- La clase Object proporciona los siguientes
métodos - void wait()
- void wait(long timeout)
- void wait(long timeout, int nanos)
- Provoca que el hilo actual espere hasta que otro
hilo invoque a notify() o notifyAll(). - void notify ()
- Despierta un solo hilo que estaba esparando en
este monitor del objeto. - void notifyAll ()
- Despierta todos los hilos que estaban esperando
en este monitor del objeto.
28Prioridad
- El planificador de hilos utiliza las prioridades
de los hilos para determinar cuándo debe permitir
que se ejecute cada hilo. - Si dos hilos están preparados para ejecutarse se
ejecutará el de mayor prioridad. - Prioridad es un valor entero (5 por defecto)
comprendido entre Thread.MIN_PRIORITY y
Thread.MAX_PRIORITY. - Se puede gestionar mediante los métodos
- final void setPriority(int nivel)
- final int getPriority ()
29Sincronización
- Cuando dos o más hilos necesitan acceder de
manera simultánea a un recurso compartido,
necesitan asegurarse de que sólo uno de ellos
accede al mismo en un instante dado. El proceso
mediante el cual se consigue esto se llama
sincronización. - Java proporciona un soporte único, a nivel de
lenguaje, para la sincronización. - Un monitor es un objeto que se utiliza como
cerrojo exclusivo, o mutex (mutually exclusive,
mutuamente exclusivo). - Sólo uno de los hilos puede ser el propietario
del monitor en un instante dado. - Cuando un hilo adquiere un cerrojo, se dice que
ha entrado en el monitor. Los restantes hilos que
estuviesen intentando acceder al monitor
bloqueado quedan en suspensión hasta que el
primer hilo salga del monitor. - Se dice que estos hilos están esperando al
monitor. Un hilo que posea un monitor puede
volver a acceder al mismo si así lo desea. - La sincronización del código se puede realizar
mediante la palabra clave synchronized
(sincronizado).
30Ejemplo sin sincronización
- //Este programa no está sincronizado.
- class Llamada
- void llama(String msg)
- System.out.print(""msg)
- try
- Thread.sleep(1000)
- catch(InterruptedException e)
- System.out.println("Interrumpido")
-
- System.out.print("")
-
-
- class ElQueLlama implements Runnable
- String msg
- Llamada objetivo
- Thread t
- public ElQueLlama(Llamada objet, String s)
- objetivo objet
- msg s
class Sincro0 public static void
main(String args) Llamada objetivo new
Llamada() ElQueLlama ob1 new
ElQueLlama(objetivo, "Hola") ElQueLlama ob2
new ElQueLlama(objetivo, "Mundo") ElQueLlama
ob3 new ElQueLlama(objetivo, "Sincronizado")
//Espera a que los hilos terminen try
ob1.t.join() ob2.t.join() ob3.t.join()
catch(InterruptedException e) System.out.prin
tln("Interrumpido") La salida
producida por este programa. HolaMundoSincroniz
ado
31Con Sincronización
- class Llamada
- synchronized void llama(String msg)
- //
-
- Esto evitará que otros métodos puedan acceder a
llama() mienta otro lo está utilizando. - La salida
- Hola
- Mundo
- Sincronizado
- Otra forma es sincronizar un objeto en un
conjunto de sentencias. - synchronized (objeto)
- //Sentencias que deben ir sincronizadas
-
32Modelado UML para la programación multihilo.
Clases activas.
- Las clases activas son elementos estructurales de
los bloques del construcción del modelo
conceptual de UML. - Una clase activa es una clase cuyos objetos
tienen uno o más procesos o hilos que constituyen
flujos de control independientes pero
concurrentes con otros flujos de control (con los
que muy probablemente se deberán sincronizar). - Una clase activa es igual que una clase, excepto
en que sus objetos representan elementos cuyo
comportamiento es concurrente con otros
elementos.
33Modelado UML para la programación multihilo.
Clases activas.