multitareas hilos – procesos locks monitores deadlocks ejemplos: productor y consumidor

Post on 03-Jan-2015

19 Views

Category:

Documents

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

Multitareas Hilos – Procesos Locks Monitores Deadlocks Ejemplos: productor y consumidor

Multitasking es lo que permite que varias actividades ocurran concurrentemente en una computadora

Habiltualmente se ditinguen:₋ Multitasking basada en procesos₋ Multitasking basada en threads (hilos)

Process-based multitasking₋ Permite que los procesos (por ejemplo programas)

corran concurrentemente en una computadora Thread-based multitasking

₋ Permite que partes(tareas) del mismo programa corran concurrentemente en una computadora

₋ La secuencia de código ejecutado para cada tarea define un camino(path) de ejecución independiente llamado thread(hilo) de ejecución.

En un ambiente que no es multi-thread (single-threaded environment), solo una tarea se ejecuta por vez.

Se desperdician ciclos de CPU por ejemplo esperando por entrada de datos del usuario

Multitasking permite aprovechar esos ciclos desperdiciados.

Algunas ventajas del multitasking basado en threads en comparación con el basado en procesos:₋ Los threads comparten el mismo espacio de

direcciones₋ El cambio de contexto entre threads es

habitualmente menos costoso que entre procesos₋ El costo de la comunicación entre threads es

relativamente bajo

Java soporta thread-based multitasking y proveee facilidades de alto nivel para la programación multihilos

Thread safety es un término que se utiliza para describir el diseño de clases que aseguran que el estado de sus objetos es siempre consistente, aún cuando sean utilizados concurrentemente por múltiples threads

Comparten el proceso que corre el programa

Cada thread en Java es creado y controlado por un único objeto de la clase java.lang.Thread

Los threads hacen que el ambiente de ejecución sea asíncrono, permitiendo realizar distintas tareas concurrentemente

El entorno de ejecución distingue entre threads del usuario y “deamon” threads (demonios)₋ Mientras un hilo del usuario esté vivo, la JVM no

termina₋ Los demonios ejecutan mientras haya threads del

usuario activos, existen solo para servir a otros threads de usuario

Cuando corremos una aplicación standalone, automáticamente se crea un thread del usuario para ejecutar el método main

Este thread se llama “main thread”

Si no se crean otros threads a partir de este, entonces el programa finaliza cuando se termine de ejecutar el método main

Si se crean otros threads a partir del main thread, estos heredan el estado de thread de usuario.

En este último caso, programa no finalizará cuando se termine el main, sino cuando finalicen todos los threads de usuario

Los threads en java son representados por un objeto de la clase Thread

Se puede implementar un thread de alguna de las siguientes formas:₋ Implementando la interfaz java.lang.Runnable₋ Extendiendo la clase java.lang.Thread

La interfaz tiene la siguiente especificación:₋ public interface Runnable {

void run(); }

Un thread que sea creado implementando esta interfaz ejecutará el código definido en el método public run()

Una clase implementa la interfaz Runnable y provee el método run() que va a ser ejecutado por el thread. Este objeto es un objeto Runnable.

Se crea un objeto de la clase Thread pasándole un objeto Runnable como argumento al constructor.

Se invoca al método start() sobre el objeto Thread. Este método retorna inmediatamente luego de que el nuevo Thread comienza a ejecutar.

Es método run() del objeto Runnable es ejecutado (eventualmente) por el objeto Thread.

Una clase extiende Thread sobrescribiendo el método run() para definir el código a ejecutar por el Thread.

Esta sublcase puede llamar al constructor de la clase Thread utilizando la llamada super()

El método start() heredado de la clase Thread es invocado sobre el objeto que extiende Thread para que este se convierta en elegible para ser ejecutado.

Implementando Runnable:₋ Mejor Diseño orientado a objeto₋ Herencia simple o individual ₋ Consistencia

Extendiendo Thread:₋ Código más sencillo

Thread(Runnable threadTarget)

static Thread currentThread()

final String getName()

final void setName(String name)

void run()

final void setDaemon(boolean flag)

final boolean isDaemon()

Los Threads comparten el mismo espacio de direcciones, por ende, pueden compartir recursos.

Hay situaciones críticas en las que se desea que solo un thread a la vez pueda acceder a un recurso compartido.

Java provee mecanismos de sincronización para controlar el acceso a recursos compartidos

class Counter { private int count = 0; public void increment() {

int n = count; count = n+1;

} }

¿Que pasaría si dos threads comparten un objeto Counter c y ambos intentan ejecutar c.increment() ?

Un lock (o monitor) se utiliza para sincronizar el acceso a un recurso compartido.

Puede asociarse a un recurso compartido.

Los Threads ganan acceso al recurso cuando son los primeros en obtener el lock asociado al mismo

Los locks logran exclusión mutua

En Java todos los objetos tienen un lock

El lock de cualquier objeto puede ser utilizado para implementar exclusión mutua.

Asociando un recurso compartido con un objeto Java y su lock, el objeto actúa como guardia asegurando acceso sincronizado al recurso

Solo un thread a la vez podrá acceder al recurso controlado por el objeto lock

La palabra clave synchronized y el lock forman las bases para implementar ejecución sincronizada.

Existen dos variantes:₋ Métodos synchronized₋ Bloques synchronized

Si un método de un objeto debe ser ejecutado de a un thread a la vez, entonces la declaración deberá tener la keyword synchronized

Un thread que intente ejecutar este método deberá primero obtener el lock del objeto antes de poder ejecutar

El lock se “solicita” invocando el método

Mientras un thread está dentro de un método synchronized, todos los demás threads que intenten ejecutar este método u otros métodos synchronized del objeto deberán esperar

Esta restricción no se aplica al objeto que tenga el lock

Los métodos estáticos sincronizan con el lock de la clase (independiente del lock de los objetos de la clase)

public Object pop() { synchronized (this) {

// Synchronized block on current // object

// ... }

} También puede especificarse el lock de una clase:

₋ synchronized (<class name>.class) { <code block> }

Los siguientes dos segmentos de código son equivalentes:

public void push(char c) {synchronized(this) {:}

}

public synchronized void push(char c) {:

}

Testeando threads:₋ isAlive() – determina si el hilo está vivo.

Accediendo a thread priority:₋ getPriority()₋ setPriority()

Poniendo threads en espera:₋ Thread.sleep()₋ join()₋ Thread.yield()

public static void main(String[] args) { Thread t = new Thread(new Runner());

t.start(); ... // Do stuff in parallel with the other thread for a while

... // Wait here for the timer thread to finish try { t.join(); } catch (InterruptedException e) {

// t came back early } ... // Now continue in this thread

... }

public class MyThread extends Thread { public void run() { while (running) {

// do lots of interesting stuff try { sleep(100); } catch (InterruptedException e) {

// sleep interrupted } } }

public static void main(String args[]) { Thread t = new MyThread(); t.start(); } }

Es cuando dos threads esperan cada uno por un lock de parte del otro

No es detectado o evitado

Puede ser evitado mediante:₋ Decidir el orden para obtener locks₋ Adherirse a este orden durante todo el proceso₋ Liberar los locks en el orden inverso

Escenario:₋ Considere que ud. y el chofer de un taxi son dos

threads

El problema:₋ Cómo determinar cuándo está ud. en su destino?

La solución:₋ Le comunica al chofer sobre su destino y se relaja₋ El taxista conduce y lo notifica cuando arriba a su

destino

Los métodos utilizados son wait y notify

Existen dos pooles:₋ wait₋ lock

Deja los datos compartidos en un estado consistente

Asegura que los programas no pueden estancarse

No coloca threads que esperan diferentes notificaciones en el mismo pool de wait

public void run() { char c;

for (int i = 0; i < 200; i++) { c = (char)(Math.random() * 26 +'A'); theStack.push(c); System.out.println("Producer" + num + ": " + c); try { Thread.sleep((int)(Math.random() * 300)); } catch (InterruptedException e) { // ignore it

} } }

public void run() { char c; for (int i = 0; i < 200; i++) { c = theStack.pop(); System.out.println("Consumer" + num + ": " +

c);

try { Thread.sleep((int)(Math.random() * 300)); } catch (InterruptedException e) { }

} }

public class SyncStack {private List buffer = new ArrayList(400);public synchronized char pop() {}public synchronized void push(char c) {}

}

public synchronized char pop() { char c; while (buffer.size() == 0) { try { this.wait(); } catch (InterruptedException e) { // ignore it...

} } c = ((Character)buffer.remove(buffer.size()-

1)).charValue(); return c; }

public synchronized void push(char c) {this.notify();Character charObj = new Character(c);buffer.addElement(charObj);

}

A continuación veremos un ejemplo clásico: Productor/Consumidor. Tenemos 2 productores y 2 consumidores, los cuales utilizan un mismo recurso.

En este ejemplo se, maneja la concurrencia al recurso compartido, el cual es producido por prodT1 y prodT2 y consumido por c1 y c2.

La clase Productor, implementa la interfaz Runnable. En su método run, produce todos los elementos y los coloca en la pila.

Entre que coloca un elemento y otro, se ejecuta el método sleep, de manera de dormir el thread por unos instantes, de manera que otros productores tengan acceso a la pila para colocar sus elemento producidos, o bien un consumidor consuma de la misma.

Ahora veremos el código correspondiente a un Consumidor.

Análogamente, el Consumidor también implementa la interfaz runnable. El método run, es muy similar al de la clase Productor, solo que en este caso, se consume de la pila.

La clase syncstack, es quien maneja la concurrencia al stack, mediante los métodos push y pop, ya que hace uso de los métodos wait() y notify(), de manera que mediante el wait, el thread espera para obtener el bloqueo y el notify, le avisa que ya termino y el recurso esta libre.

A continuación veremos un ejemplo.

Producer2: FConsumer1: FProducer2: KConsumer2: KProducer2: TProducer1: NProducer1: VConsumer2: VConsumer1: NProducer2: VProducer2: UConsumer2: UConsumer2: VProducer1: FConsumer1: FProducer2: MConsumer2: MConsumer2: T

Multitareas Hilos – Procesos Locks Monitores Deadlocks Ejemplos: productor y consumidor

The Java TutorialTrail de Threadshttp://java.sun.com/docs/books/tutorial/index.html

top related