1. Fundamentos. 2. El modelo de hilo de Java.

Anuncio
Pr ogr amac ión A v anz ada
Hilos
Hi los
1. Fundamentos.
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 (multitasking).
Los sistemas de un solo hilo utilizan un enfoque llamado bucle de suceso con sondeo.
Cuando un hilo se bloquea deteniendo su ejecución, el programa completo se
detiene.
Existen dos tipos distintos de multitarea:
1.- Basada en procesos:
Permite a la computadora ejecutar más de un proceso concurrentemente.
La unidad de código más pequeña es el proceso.
Los procesos son tareas pesadas que necesitan su propio espacio de
direccionamiento.
La comunicación entre procesos es cara y limitada.
2.- Basada en hilos:
Un programa simple puede realizar más de una tarea simultáneamente.
La unidad de código más pequeña es el hilo.
Los hilos (procesos ligeros) son más ligeros que los procesos (procesos pesados).
Comparten el mismo espacio de direcciones y el mismo proceso pesado.
La comunicación entre hilos es ligera y el cambio contexto de un hilo al siguiente
es menos costoso.
Java hace uso de entornos multitarea basados en procesos, pero la multitarea basada en
hilos está bajo el control de Java.
2. El modelo de hilo de Java.
Un hilo puede estar en uno de los siguientes estados:
Ejecutándose.
Preparado para ejecutarse, tan pronto como se disponga de tiempo de CPU.
Suspendido , su ejecución se detiene temporalmente.
Reanudarse, permitiendo que continúe su tarea donde la dejó.
Bloqueado , cuando está a la espera de algún recurso.
El intérprete de Java utiliza las prioridades para determinar cómo debe tratar cada hilo
con respecto a los demás. La prioridad de un hilo se utiliza para decidir cuándo se
pasa a ejecutar otro hilo (cambio de contexto).
Las reglas que determinan un cambio de contexto son las siguientes:
Flo r ent ino Fdez. Rivero la
Dpto . I nfo rm át ica
UN IVE RSIDA D DE VIG O
1
Pr ogr amac ión A v anz ada
•
•
•
Hilos
Un hilo puede ceder voluntariamente el control por abandono explícito, al
quedarse dormido o al bloquearse en espera de una operación de E/S pendiente.
=> Se examinan los hilos restantes y se selecciona aquel que tenga mayor
prioridad.
Un hilo que no libera la CPU puede ser desalojado por otro de mayor prioridad.
=> (multitarea por desalojo).
En el caso de hilos de igual prioridad que compiten por la CPU, depende del S.O.
(¡precaución!).En Windows 95 los hilos de igual prioridad se reparten la CPU
mediante un algoritmo circular (round-robin). En Solaris 2.x los hilos de igual
prioridad deben ceder el control voluntariamente, sino los demás hilos no se
ejecutarán.
SINCRONIZACIÓN
Java implementa una versión de un modelo clásico de sincronización entre procesos,
llamado monitor (definido inicialmente por Hoare, y que puede entenderse como una
pequeña caja negra en la que sólo cabe un hilo).
Los monitores se utilizan para proteger un recurso compartido y evitar que sea
manipulado por más de un hilo simultáneamente.
En Java cada objeto tiene su propio monitor, en el que se entra 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.
INTERCAMBIO DE MENSAJES
Java proporciona métodos predefinidos que permiten que un hilo entre en un método
sincronizado de un objeto, y espere ahí hasta que otro hilo le notifique explícitamente
que debe salir de él.
LA CLASE TREAD Y LA INTERFAZ RUNNABLE
Para crear un nuevo hilo en Java, el programa deber heredar de la clase Thread o
implementar la interfaz Runnable.
Los métodos básicos para gestionar los hilos se enumeran a continuación:
public final String getName()
Obtiene el nombre de un hilo.
public final int getPriority()
Obtiene la prioridad de un hilo.
public final native boolean isAlive()
Comprueba si un hilo se está ejecutando todavía. Devuelve true si todavía está
ejecutándose o false en caso contrario.
Flo r ent ino Fdez. Rivero la
Dpto . I nfo rm át ica
UN IVE RSIDA D DE VIG O
2
Pr ogr amac ión A v anz ada
Hilos
public final void join() throws InterruptedException
Espera hasta que finalice el hilo sobre el que se llama.
public final void resume()
Reanuda la ejecucuión de un hilo suspendido
public void run()
Punto de entrada de un hilo.
public static native void sleep(long millis) throws InterruptedException
Suspende un hilo durante un período de tiempo
public native synchronized void start()
Comienza un hilo llamando a su método run().
public final void stop()
Detiene la ejecución de un hilo.
public final void suspend()
Suspende un hilo.
3. El hilo principal. Creación de un hilo.
Cuando se ejecuta un programa Java ya existe un hilo en ejecución, llamado hilo
principal. Este hilo es especial por dos razones:
•
•
Desde él se crearán el resto de hilos del programa.
Debe ser el último hilo que termine su ejecución. (Si un hilo principal finaliza
antes que un hijo Java puede bloquearse, hang).
El método run() es el punto de entrada de un nuevo hilo de ejecución concurrente dentro
de un programa. El hilo termina cuando finalice el método run().
Para que un hilo comience su ejecución se debe llamar a su método start().
La mayoría de los programadores en Java crean hilos heredando de la clase Thread
cuando modifican o mejoran dicha clase. Si lo que se necesita es simplemente la
funcionalidad de un hilo, lo normal es implementar la interfaz Runnable.
4. Creación de múltiples hilos. isAlive(), join(), suspend(), resume().
Los programas en Java pueden generar tantos hilos como necesiten.
Para asegurarse de que un hilo hijo ha terminado se pueden utilizar dos métodos:
isAlive() y join().
Flo r ent ino Fdez. Rivero la
Dpto . I nfo rm át ica
UN IVE RSIDA D DE VIG O
3
Pr ogr amac ión A v anz ada
Hilos
Se puede pasar un hilo que está ejecutándose al estado “suspendido” mediante la
llamada al método suspend(), para reanudarlo se llama al método resume(). Esto
puede ser útil por ejemplo, en el caso de un hilo que implemente un reloj en pantalla.
Si el usuario no desea el reloj, el hilo puede suspenderse.
5. Prioridades. setPriority(), getPriority().
La manera en la que un S.O. implementa la multitarea puede afectar al tiempo de CPU
disponible para cada hilo.
Si se desea lograr un comportamiento predecible sobre distintas plataformas, se deben
programar hilos que voluntariamente cedan el control a la CPU. En concreto, los
hilos que comparten la misma prioridad deben ceder el control de vez en cuando para
asegurar que todos los hilos tengan oportunidad de ejecutarse en un S.O. con
multitarea no apropiativa (non-preemptive).
Cuando está ejecutándose un hilo de baja prioridad y otro de mayor prioridad se
despierta de su sueño o de la espera de cierta operación de E/S, el hilo de menor
prioridad es desalojado.
Para asignar una prioridad a un hilo se utiliza el método setPriority(). Los posibles
valores están comprendidos entre MIN_PRIORITY (1) y MAX_PRIORITY (10).
La constante NORM_PRIORITY (5) establece la prioridad por defecto. Para
obtener la prioridad de un hilo se utiliza el método getPriority().
6. Sincronización. La sentencia synchronized.
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 hecho de que varios hilos llamen al mismo método sobre el mismo objeto a la vez se
denomina “condición de carrera” (race codition).
En C/C++ la sincronización de procesos se realiza mediante llamadas a primitivas del
sistema operativo. Java implementa la sincronización a través de elementos del
propio lenguaje (métodos synchronized).
Durante todo el tiempo que un hilo permanezca dentro de un método sincronizado, los
demás hilos que intenten acceder a ese método sobre la misma instancia, tendrán que
esperar.
Para sincronizar el acceso a objetos de una clase que no fue diseñada para acceso
multihilo, se realizan las llamadas a los métodos dentro de un bloque sincronizado:
synchronized(objeto)
{
Flo r ent ino Fdez. Rivero la
Dpto . I nfo rm át ica
UN IVE RSIDA D DE VIG O
4
Pr ogr amac ión A v anz ada
Hilos
// sentencias que deben ser sincronizadas;
}
7. Comunicación entre hilos.
Java proporciona un mecanismo de comunicación entre procesos a través de los
métodos wait(), notify(), y notifyAll() definidos en la clase Object (todas las clases
disponen de ellos).
Cualquiera de estos tres métodos sólo puede ser llamado desde dentro de un método
synchronized.
public final void wait() throws InterruptedException
Le indica al hilo en curso que abandone el monitor y se vaya a dormir hasta que otro
hilo entre en el mismo monitor (algún método sinchronized de la misma clase) y
llame a notify(). Existen otras formas para este método donde se permite
especificar un período de tiempo de espera.
public final native void notify()
Despierta al primer hilo que realizó una llamada a wait() sobre el mismo objeto.
public final native void notifyAll()
Despierta todos los hilos que realizaron una llamada a wait() sobre el mismo objeto.
El hilo con mayor prioridad de entre los que han sido despertados es el primero en
ejecutarse.
Un buen ejemplo es el problema del productor/consumidor modificado de forma que el
productor tiene que esperar a que el consumidor haya terminado para empezar a
producir más datos.
8. Bloqueos. deadlock.
Este tipo de error está relacionado con la multitarea y es necesario evitarlo. Se produce
cuando dos hilos tienen una dependencia circular en un par de objetos sincronizados.
El bloqueo es un error difícil de resolver por dos razones:
•
•
Ocurre en raras ocasiones, cuando los dos hilos intentan entrar a la vez.
En el bloqueo pueden estar involucrados más de dos hilos y dos objetos
sincronizados.
Flo r ent ino Fdez. Rivero la
Dpto . I nfo rm át ica
UN IVE RSIDA D DE VIG O
5
Descargar