55
Concurrencia en Java Luis Gajardo [email protected] Sistemas Operativos

04.Concurrencia en Java

Embed Size (px)

Citation preview

Page 1: 04.Concurrencia en Java

Concurrencia

en Java

Luis Gajardo

[email protected]

Sistemas

Operativos

Page 2: 04.Concurrencia en Java

DESDE JAVA 1.4

Page 3: 04.Concurrencia en Java

HEBRAS EN JAVA

Creación de hebras

• Especificar el comportamiento de una hebra:

• Escribir la clase A que implementa la interfaz Runnable y redefinir el método run()‏

• Crear el objeto que representa el comportamiento: Runnable runnable= new A();

• Crear el controlador de la hebra: Thread thread= new Thread(runnable);

• Lanzar la hebra: thread.start();

Page 4: 04.Concurrencia en Java

ESPECIFICAR EL COMPORTAMIENTO

• Ejemplo:

class SomeBehavior implements Runnable {

String name;

SomeBehavior(String name) {

this.name= name;

}

public void run() {

for (int i= 0; ; i++)‏ System.out.println(name+": i= "+i);

}

}

Page 5: 04.Concurrencia en Java

CREACION DE LA HEBRA

• Ejemplo:

public class Example {

public static void main(String[] args) {

Runnable r= new SomeBehavior("A");

Thread t= new Thread(r);

t.start();

for (int i= 0; i<4; i++)‏ System.out.println("main: i= "+i);

}

}

Page 6: 04.Concurrencia en Java

EJECUCION

• Ejemplo:

% java Example

main: i= 0

La salida de ambas hebras puede mezclarse

Una aplicación en Java termina cuando todas

sus hebras terminan

El método main termina, pero no la hebra

adicional

Nunca se obtiene el “%"

A: i= 0

main: i= 1

A: i= 1

main: i= 2

main: i= 3

A: i= 2

A: i= 3

A: i= 4

A: i= 5

... (nunca termina) ...

Page 7: 04.Concurrencia en Java

ESPERAR A QUE UNA HEBRA TERMINE

• Una hebra termina cuando el método run() termina, o cuando uno de

sus métodos lanza una excepción no capturada

• Una segunda hebra puede esperar que la hebra t termine invocando: t.join();

• Si t ya terminó, join retorna de inmediato

• Incomodidad:

join lanza InterruptedException

Page 8: 04.Concurrencia en Java

EJEMPLO: Cálculo de Fibonacci // Cálculo de un número de Fibonacci secuencialmente

static int seqFib(int n) {

if (n<=2)‏ return 1;

else

return seqFib(n-1)+seqFib(n-2);

}

// Cálculo de Fibonacci concurrente

static int dualFib(int n) {

try {

if (n<=20)‏ return seqFib(n);

else {

FibCalculator calc= new FibCalculator(n-2);

Thread t= new Thread(calc);

t.start();

int partialRes= seqFib(n-1);

t.join(); // lanza InterruptedException

return partialRes + calc.res;

}

}

catch (InterruptedException excp) { return 0; }

}

Page 9: 04.Concurrencia en Java

EJEMPLO: Cálculo de Fibonacci

// Comportamiento de la hebra

static class FibCalculator implements Runnable {

int n;

int res;

FibCalculator(int n) {

this.n= n;

}

public void run() {

res= seqFib(n);

}

}

Page 10: 04.Concurrencia en Java

EJEMPLO: Cálculo de Fibonacci

Observaciones:

• Calcular fib concurrentemente para n<=20 es inútil:

- Crear una hebra es tan caro como calcular fib(20).

• Recomendación:

- Evite el enredo de código debido a la captura de excepciones

en la mitad de un método, coloque la instrucción try ... catch en

el nivel más amplio del método

Page 11: 04.Concurrencia en Java

PATRONES DE LA CONCURRENCIA

Patrón Futuros

• Una mejor estructura para calcular fib en paralelo:

static int dualFib(int n) {

if (n<=20)‏ return seqFib(n);

else {

FutureFib future= new FutureFib(n-2);

int partialRes= seqFib(n-1);

return partialRes + future.get();

}

}

Page 12: 04.Concurrencia en Java

PATRONES DE LA CONCURRENCIA Patrón Futuros (continuación)‏

class FutureFib implements Runnable {

int n;

Thread t;

int res;

FutureFib(int n) {

this.n= n;

t= new Thread(this);

t.start();

}

public void run() {

res= seqFib(n);

}

public int get() {

try {

t.join();

return res;

}

catch (InterruptedException excp) { return 0; }

} }

Page 13: 04.Concurrencia en Java

PATRONES DE LA CONCURRENCIA

Patrón Futuros: Generalización

class FutureComputation implements Runnable {

declarar parámetros

tipo-retorno res;

Thread t;

FutureComputation(parámetros) {

asignar parámetros

t= new Thread(this);

t.start();

}

public void run() {

res= calcular

}

tipo-retorno get() {

try {

t.join();

return res;

}

catch (InterruptedException excp) { ... }

} }

Page 14: 04.Concurrencia en Java

PATRONES DE LA CONCURRENCIA

Patrón Futuros: Ejercicio propuesto factorial

• Implementar el método: static double factorial(int n){ ... }

• factorial(n)= 1*2*3* ... * (n-1) * n

calcular (n/2+1) * ... * (n-1) * n como un futuro.

• Usted puede usar:

static double seqProd(int i, int j) {

double res= 1.0;

for (int k= i; k<=j; k++)‏ res*= k;

return res;

}

Page 15: 04.Concurrencia en Java

CREACION DE HEBRAS

Mecanismo alternativo

• Extender la clase Thread para especificar el comportamiento de la

hebra en el mismo controlador de la hebra:

class MyThread extends Thread {

...

public void run() {

//... actividades de la hebra ...

}

}

• Crear el controlador de la hebra: MyThread thread= new MyThread();

• Lanzar la hebra: thread.start();

Page 16: 04.Concurrencia en Java

MONITORES EN JAVA

• En Java, cada objeto es conceptualmente un monitor.

• Invariante: Una sola hebra puede poseer el monitor

• Un monitor es solicitado y devuelto mediante la instrucción especial:

synchronized ( expresión ) {

/*cuerpo*/

}

• La evaluación de expresión entrega la referencia del objeto que

constituye el monitor.

Page 17: 04.Concurrencia en Java

MONITORES EN JAVA

Semántica de la instrucción synchronized

• Una hebra T ejecuta la instrucción synchronized de la siguiente

manera:

• T evalúa expresión. Sea M el monitor referenciado por la

expresión.

• T solicita el monitor M. T podría tener que esperar, debido a otras

hebras compitiendo por M.

• T ejecuta el cuerpo de la instrucción synchronized.

• T devuelve el monitor M.

• T posee el monitor M mientras se ejecuta el cuerpo o cualquier

método invocado desde el cuerpo

• Java garantiza que M siempre se devuelve cuando se sale de la

instrucción synchronized

• M es otorgado en orden de llegada

Page 18: 04.Concurrencia en Java

MONITORES EN JAVA

Devolución temporal del monitor

• En ocasiones una operación no puede ejecutarse mientras no se

verifique una condición.

• Para postergar una operación:

M.wait(); // El monitor es devuelto

• Para notificar que una condición podría verificarse:

M.notifyAll(); // Se retoman todas las hebras postergadas

• El thread que invoca M.notifyAll() mantiene la propiedad del monitor

Page 19: 04.Concurrencia en Java

MONITORES EN JAVA

Reactivación de hebras postergadas

• Todos las hebras que invocaron M.wait() son retomadas.

• Pero antes de continuar solicitan el monitor M.

• Ejemplo:

Object get() {

synchronized (this) {

try {

while (list.isEmpty())

this.wait(); // lanza InterruptedException

return list.remove(0);

}

catch (InterruptedException excp) {

return null;

}

}

}

Page 20: 04.Concurrencia en Java

PATRON DE SINCRONIZACION: GUARDIAS

• Un guardia es una condición que debe verificar un objeto para que una

operación pueda ejecutarse.

• La mayoría de los problemas de sincronización pueden modelarse a

partir de guardias.

• Ejemplos de guardias:

La operación get sólo puede ejecutar cuando se verifica:

!list.isEmpty()

La operación put sólo puede ejecutar cuando se verifica:

list.size()<MAXSIZE

Page 21: 04.Concurrencia en Java

PATRON DE SINCRONIZACION: GUARDIAS

El buffer correcto

class Buffer {

List list= new ArrayList();

Synchronized void put(Object item) {

try {

while (list.size()>=MAXSIZE)

wait();

list.add(item);

notifyAll();

}

catch(InterruptedException e) {

}

}

synchronized Object get() {

try {

while (list.isEmpty())

wait();

Object item= list.remove(0);

notifyAll();

return item;

}

catch(InterruptedException e) {

return null;

} } }

Page 22: 04.Concurrencia en Java

PATRON DE SINCRONIZACION: GUARDIAS

Generalización: Guardias

Cuando requiera sincronización, trate de usar el siguiente patrón:

synchronized tipo nombre-método(parámetros) {

try {

while (! guardia)

wait();

... ejecute operación ...

notifyAll();

}

catch (InterruptedException excp) {

return ...;

}

}

Page 23: 04.Concurrencia en Java

OBSERVACIONES SOBRE NotifyAll()

• Cuando la operación es siempre ejecutable, no coloque el while

• No invoque notifyAll cuando puede probar que ninguna guardia va a

cambiar.

• notifyAll() retoma todas las hebras postergadas.

• Parece ineficiente y es ineficiente.

• ¿Es posible retomar una sola hebra?

No use notify • notify() retoma una sola hebra, no sabemos cuál.

• Es muy difícil ver la condición de borde que hará que su solución no

funcione, pero existe.

• notifyAll() es ineficiente pero no se puede evitar. Es un defecto de

diseño de Java

• Los monitores de Java se inspiran de las regiones críticas de P.

Brinch Hansen (1972).

• J. Gosling olvidó que Car Hoare mejoró los monitores en 1974

• JDK 1.5 trae los monitores de Hoare.

Nota: J. Gosling es el creador de Java

Page 24: 04.Concurrencia en Java

SOLUCION PROBLEMA DE LOS FILOSOFOS

• Los 5 filósofos son representados por 5 hebras.

• Hay un monitor controlando el acceso a los palitos.

Hebras-

filósofo

Solicitudes

de palitos monitor

controlador

El controlador

class Controller {

boolean[] state= new boolean[5];

synchronized void take(int k) {

try {

while (state[k])

wait();

state[k]= true;

}

catch(InterruptedException e){

}

}

synchronized void leave(int k) {

state[k]= false;

notifyAll();

}

}

public void run() {

for (;;) {

int first= Math.min(i, (i+1)%5);

int last= Math.max(i, (i+1)%5);

ctrl.take(first);

ctrl.take(last);

eat(i, (i+1)%5);

ctrl.leave(i);

ctrl.leave((i+1)%5);

think();

} }

Sea consiente que en esta solución los filósofos pueden sufrir hambruna

Page 25: 04.Concurrencia en Java

DESDE JAVA 1.5

Page 26: 04.Concurrencia en Java

CONCURRENCIA EN JAVA 1.5

• La plataforma Java 2 incluye nuevas utilidades para el manejo de

concurrencia, desde la versión 1.5.

• Se han añadido los paquetes:

java.util.concurrent

java.util.concurrent.atomic

java.util.concurrent.locks

Estos paquetes permiten crear diversas estructuras de hilos, como:

pooling de hilos o colas de bloqueos, liberando al programador del

control "a mano".

En definitiva, se da soporte para automatizar la programación

concurrente.

Page 27: 04.Concurrencia en Java

FRAMEWORK EXECUTOR

• Un Executor es un objeto que ejecuta tareas de tipo Runnable.

• Es similar a la invocación:

new Thread(aRunnableObject).start();

• Dos observaciones:

• La creación de threads puede ser costosa

• Un mecanismo óptimo para la mantención de threads es difícil de

implementar.

• Solución:

• El nuevo framework Executor resuelve todos estos problemas separando

la utilización del thread de la forma en cómo este debe ser creado.

• Además permite estandarizar la invocación, planificación, ejecución y

control de tareas asíncronas según un conjunto de políticas de ejecución.

Page 28: 04.Concurrencia en Java

FRAMEWORK EXECUTOR

Creando un Executor

• Crearlo usando un método factory de la clase Executors.

• Por ejemplo:

• Executors.newCachedThreadPool()

Crea un pool que va a ir creando threads conforme se vayan necesitando,

pero puede reutilizar threads inactivos creados anteriormente.

• Executors.newFixedThreadPool(int numThreads)

Crea un pool con el número de threads indicado; dichos threads siempre

estarán listos para procesar tareas. El pool maneja también una cola de

tareas; cada thread toma una tarea de la cola y la procesa, al terminar

continúa con otra tarea de la cola hasta que no queden más.

• Otros executors

• ScheduledThreadPool, SingleThreadExecutor()...

Page 29: 04.Concurrencia en Java

FRAMEWORK EXECUTOR

Usando un Executor

• Por ejemplo:

Executor executor = Executors.newFixedThreadPool(5);

executor.execute (new RunnableTask1());

executor.execute (new RunnableTask2());

• Para detener (ordenadamente) todos los threads en ejecución, usar:

executor.shutdown();

• Otros métodos de interés:

• shutdownNow()

• awaitTermination()

Page 30: 04.Concurrencia en Java

FRAMEWORK EXECUTOR

• Ejemplo: un simple pool de threads (conjunto de hilos) con un tamaño por

defecto de 2 y un máximo de 4 hilos. El método shutdown() termina todos los

hilos si no hay tareas en ejecución. Por lo tanto, Executor libera al

programador de la gestión de los threads y nos permite centrarnos en la

funcionalidad.

public class SimplePooledExecutorSample {

private ThreadPoolExecutor executor;

private int defaultThreadCount=2;

private int maxThreadCount=4;

private int keepAliveTime=100;

private int MAX_PENDING_TASKS=100;

private BlockingQueue pendingTasksQueue= new ArrayBlockingQueue(MAX_PENDING_TASKS);

public SimplePooledExecutorSample() {

executor = new ThreadPoolExecutor(defaultThreadCount,maxThreadCount,

keepAliveTime,TimeUnit.MILLISECONDS,pendingTasksQueue );

}

public void createMultipleProducerThreads(int producerThreadCount ) {

Producer c= new Producer();

for (int i=0; i < producerThreadCount; i++ ){

executor.execute(c);

}

}

Page 31: 04.Concurrencia en Java

FRAMEWORK EXECUTOR

public void createMultipleConsumerThreads(int consumerThreadCount ) {

Consumer c= new Consumer();

for (int i=0; i < consumerThreadCount; i++ ){

executor.execute(c);

}

}

public void shutdown(){

executor.shutdown();

}

public static void main(String[] args ){

SimplePooledExecutorSample sample= new SimplePooledExecutorSample();

sample.createMultipleProducerThreads(3);

sample.createMultipleConsumerThreads(3);

sample.createMultipleProducerThreads(3);

sample.shutdown();

}

Page 32: 04.Concurrencia en Java

FRAMEWORK EXECUTOR

class Consumer implements Runnable {

private int runCount= 1;

public void run() {

System.out.println("Consuming..." + runCount++ +" ThreadId:" +

Thread.currentThread().getId());

}

}

class Producer implements Runnable {

private int runCount= 1;

public void run() {

for (int i=0;i<10000;i++) { //algo de actividad para mantener ocupada a la CPU..

double x=(double)100.89*(double)124.99;

}

System.out.println("Producing..." + runCount++ +" ThreadId:" +

Thread.currentThread().getId());

}

}

Page 33: 04.Concurrencia en Java

COLECCIONES CONCURRENTES

• Una colección es un objeto que agrupa múltiples elementos en una sola

unidad.

• Las colecciones se usan para almacenar, recuperar, manipular y comunicar

agregación de datos.

• Normalmente representan ítem de datos que forma un grupo natural, tal y

como una baraja de cartas, carpeta de correos o directorio telefónico.

• Collection es la interface “genérica” en java que permite realizar esta

agrupación.

• Java 1.4 posee variadas colecciones, pero no pueden ser utilizadas en

entornos concurrentes a no ser que el programador se preocupe de la

sincronización.

Page 34: 04.Concurrencia en Java

COLECCIONES CONCURRENTES

• Java 1.5 implementa colecciones concurrentes que permiten el acceso

concurrente sin que el programador se preocupe de ello.

• La interface BlockingQueue posee las siguientes implementaciones:

• ArrayBlockingQueue: Cola implementada con un arreglo que posee

límite de capacidad.

• DelayQueue: Cola en la cual los elementos pueden retirarse cuando su

delay (tiempo de espera) ha expirado.

• LinkedBlockingQueue: Cola implementada con lista enlazada, con límite

opcional de capacidad.

• PriorityBlockingQueue: cola de prioridad que ordena los elementos

automáticamente según prioridad.

• SynchronousQueue: cola sincronizada, sólo realiza la operación cuando

es posible, sino espera.

Page 35: 04.Concurrencia en Java

COLECCIONES CONCURRENTES

• Los métodos declarados en la interfaz BlockingQueue:

Para añadir elementos

• add() : agrega un elemento

• offer() : devuelve false si está llena, no genera excepción

• put() : se bloquea si la cola está llena

Para extraer elementos

• remove() : extrae el elemento del principio. Genera excepción si vacío

• poll() : igual que remove() pero si la cola está vacía devuelve null

• take() : se bloquea si la cola está vacía

• drainTo() : vacía la cola y devuelve colección

Para consultar sin extraer

• element() : devuelve, sin extraerlo, el primer elemento. Excep. si vacía

• peek() : igual que element() pero devuelve null si cola vacía

Page 36: 04.Concurrencia en Java

COLECCIONES CONCURRENTES

• Ejemplo:

class Producer implements Runnable {

private final BlockingQueue queue;

Producer(BlockingQueue q) {

queue= q

}

public void run() {

try {

while (true)‏ q.put(produce());

} catch (InterruptedException ex) {

... }

}

Object produce() {...}

}

class Consumer implements Runnable {

private final BlockingQueue queue;

Consumer(BlockingQueue q) {

queue= q

}

public void run() {

try {

while (true)‏ consume(q.take());

} catch (InterruptedException ex) {

... }

}

void consume(Object x) {...}

}

Implementación del problema del productor - consumidor

Page 37: 04.Concurrencia en Java

COLECCIONES CONCURRENTES

class Ejemplo {

void main() {

//implementación específica de la cola

BlockingQueue q= new LinkedQueueImplementation();

Producer p= new Producer(q);

Consumer c1= new Consumer(q);

Consumer c2= new Consumer(q);

new Thread(p).start();

new Thread(c1).start();

new Thread(c2).start();

}

}

Page 38: 04.Concurrencia en Java

VARIABLES ATOMICAS

(java.util.concurrent.atomic)‏

• Son clases para manipular atómicamente variables individuales (tipos

primitivos o referenciados) permitiendo el uso de métodos para

aritmética atómica y asignación de variables (test-and-set) de alto

rendimiento.

import java.util.concurrent.atomic.AtomicInteger;

class AtomicCounter {

private AtomicInteger c= new AtomicInteger(0);

public void increment() { public void decrement() {

c.incrementAndGet(); c.decrementAndGet();

} }

}

public int value() {

return c.get();

}

Page 39: 04.Concurrencia en Java

OTROS MECANISMOS DE SINCRONIZACION

• Existen otras herramientas para sincronizar procesos y proteger

secciones críticas del código:

• Semaphore – semáforos

• Locks

• countDownLatch

• CyclicBarrier

• Exchanger

Page 40: 04.Concurrencia en Java

SEMAPHORE

• Posee el mismo funcionamiento que los semáforos tradicionales, permite

controlar el número de procesos que acceden a la sección crítica.

• Crear el semáforo: (java.util.concurrent.Semaphore)

• Semaphore(int tickets) Crea el semáforo con el número de tickets indicado.

• Semaphore(int tickets, boolean justicia)

Crea el semáforo con el número de tickets indicado. Si el parámetro justicia es true,

el orden de atención de los threads en espera será FIFO sino no existe certeza

sobre cual thread será retomado.

• Operaciones de sincronización sobre el semáforo:

• acquire() Saca un ticket del semáforo si hay disponibles, sino bloquea el thread hasta que

exista uno disponible. Lanza la excepción InterruptedException.

• release() Aporta un ticket al semáforo, pero nunca bloquea el thread.

Page 41: 04.Concurrencia en Java

SEMAPHORE

• Ejemplo:

import java.util.concurrent.Semaphore ;

class Example extends Thread {

int id;

static Semaphore semaphore = new Semaphore(1);

public Example(int id) {

this.id= id;

}

public void run() {

try {

semaphore.acquire();

// SECCION CRITICA

semaphore.release();

} catch (InterruptedException e) {}

}

public static void main(String[] args) {

Example e1= new Example(1);

Example e2= new Example(2);

e1.start();

e2.start();

}}

Crea el semáforo

con un ticket

Solicita un ticket

Devuelve un ticket

Page 42: 04.Concurrencia en Java

SEMAPHORE

• Otros métodos:

• tryAcquire()

Este método es no bloqueante. Solicita un ticket, sino está disponible retorna de

inmediato al thread que lo invoca. Retorna un valor booleano que indica si tuvo

éxito (true) o no (false).

• tryAcquire(long timeout, TimeUnit unit) Este método es no bloqueante. Solicita un ticket, sino está disponible espera el

tiempo especificado en timeout para obtenerlo. Si al cabo de la espera no se ha

obtenido un ticket retorna al thread que lo invoca. El parámetro unit puede ser

SECONDS, MILLISECONDS, NANOSECONDS.

Ejemplo:

boolean exito;

try {

exito= semaphore.tryAcquire(50, TimeUnit.MILLISECONDS);

} catch (InterruptedException e) {}

if (exito) {

// SECCION CRITICA

semaphore.release();

}

Page 43: 04.Concurrencia en Java

CYCLIC BARRIER

• Este mecanismo permite establecer puntos de espera comunes para varios

threads.

• Se debe indicar a cuantos threads se esperan antes de continuar.

• Cada thread que llega al punto de espera debe esperar a que el resto de los

threads llegue.

• La clase CyclicBarrier permite implementar esta funcionalidad en Java.

• Cuando llega el último thread, todos se desbloquean y pueden continuar.

Eventualmente también se puede lanzar un nuevo thread que realice alguna

tarea (mediante la interfaz Runnable).

• Dado que son cíclicas pueden ser utilizadas varias veces (varios puntos de

espera).

Page 44: 04.Concurrencia en Java

CYCLIC BARRIER

• Los métodos son:

public CyclicBarrier(int parties);

Crea una nueva CyclicBarrier la cual se levantará (o desbloqueará) cuando

terminen los hilos (parties) que se esperan.

public CyclicBarrier(int parties, Runnable barrierAction);

Igual que el anterior, pero además ejecuta una acción al cumplirse la barrera.

public int await()

throws InterruptedException, BrokenBarrierException

Espera a todos los hilos que han invocado await() sobre la barrera.

public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException

Igual que el anterior, pero aquí es posible definir un tiempo de espera

máximo (timeout). El parámetro unit indica la unidad en la cual se ha

expresado timeout.

Page 45: 04.Concurrencia en Java

CYCLIC BARRIER

• Ejemplo:

int THREADS = 5; CyclicBarrier barrier = new CyclicBarrier(THREADS); ... barrier.await(); //sección crítica, continua solo cuando 5 threads invoquen await() barrier.await(); //otra sección crítica

Page 46: 04.Concurrencia en Java

LOCKS REENTRANTES

• Se introduce un mecanismo de sincronización alternativo al lock

normal que se define a través de la clase ReentrantLock y cuya

funcionalidad se define a través de la interfaz Lock.

• El ReentrantLock se introduce por las limitaciones del lock

normal:

- No es posible interrumpir un thread que espera un wait.

- No es posible intentar de forma no bloqueante adquirir un

lock sin suspenderse definitivamente en él.

- Los locks intrínsecos deben ser liberados en el mismo

bloque de código en el cual se suspendió.

(java.util.concurrent.locks.ReentrantLock)‏

Page 47: 04.Concurrencia en Java

LOCKS REENTRANTES

• Comparación:

- El Lock normal conduce a un estilo de programación

sencillo, seguro y compatible con la gestión de

excepciones.

- El ReentrantLock conduce a estrategias menos seguras,

pero más flexibles, proporciona mayor vivacidad y mejores

características de respuesta.

Page 48: 04.Concurrencia en Java

LOCKS REENTRANTES

La interface Lock provee varios métodos novedosos:

public interface Lock{

void lock();

// solicitar el lock

boolean tryLock();

// adquirir el lock solo si esta libre al momento de solicitarlo.

// El thread no se bloquea. Retorna true si el lock fue adquirido.

boolean tryLock(long timeout, TimeUnit unit) throws

InterruptedExcetion;

// adquirir el lock solo si esta libre en el tiempo especificado.

// El thread no se bloquea. Retorna true si el lock fue adquirido.

void unlock();

// liberar el lock

Condition newCondition();

//Crea una nueva variable de condición asociada al lock

}

Page 49: 04.Concurrencia en Java

LOCKS REENTRANTES

A diferencia del lock normal, la interface Lock ofrece la toma de un lock:

incondicional, no bloqueante, temporizado o interrumpible. Además

todas las operaciones de suspensión y liberación de un lock son

explícitas.

Ejemplo sección crítica basada en un Lock explícito:

Lock control = new ReentrantLock();

...

control.lock();

try{

// Actualiza al objeto protegido por el lock

// Atiende excepciones y restaura invariantes si es necesario

}finally{

control.unlock();

}

La liberación debe hacerse en una sentencia finally, ya que hay que

preveer la posibilidad de una excepción, y en este caso el lock debe de

liberarse explícitamente también.

Page 50: 04.Concurrencia en Java

LOCKS REENTRANTES Y VARIABLES DE CONDICION

• Cuando se utiliza un Lock explícito para definir una región asíncrona,

dentro de ella se utilizan los objetos Condition como mecanismo de

sincronización entre threads. Esto es similar a un Monitor.

• Un objeto Condition está estructuralmente ligado a un objeto Lock. Sólo

puede crearse invocando el método new Condition() sobre un objeto

Lock.

• El objeto Condition solo puede ser invocado por un thread que

previamente haya tomado el Lock al que pertenece.

Page 51: 04.Concurrencia en Java

public interface Condition {

void await();

// causa la espera del thread sobre una variable de condición

boolean await(long time, TimeUnit unit);

// causa la espera del thread, por el tiempo especificado, sobre

// una variable de condición. No se bloquea. Retorna false cuando

// no se bloquea.

void signal();

// Notifica una condición cumplida, despierta solo un thread

void signalAll();

// Notifica una condición cumplida. Despierta a todos los threads

...

}

La interface Condition provee los siguientes métodos:

LOCKS REENTRANTES Y VARIABLES DE CONDICION

Page 52: 04.Concurrencia en Java

• Ejemplo: Productor - Consumidor

class BoundedBuffer {

...

final Object[] items = new Object[100];

int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {

...

try {

...

} finally{

...

}

}

LOCKS REENTRANTES Y VARIABLES DE CONDICION

Page 53: 04.Concurrencia en Java

• Ejemplo: Productor - Consumidor

public Object take() throws InterruptedException {

...

try {

...

} finally {

...

}

}

} //fin de la clase

LOCKS REENTRANTES Y VARIABLES DE CONDICION

Page 54: 04.Concurrencia en Java

• Ejemplo: Productor - Consumidor

class BoundedBuffer {

final Lock lock= new ReentrantLock();

final ConditionnotFull= lock.new Condition();

final ConditionnotEmpty = lock.new Condition();

final Object[] items = new Object[100];

int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {

lock.lock();

try {

while(count== items.length)

notFull.await();

items[putptr] = x;

putptr= (puptr+1) % items.length;

count= count+1;

notEmpty.signal();

} finally{

lock.unlock();

}

}

LOCKS REENTRANTES Y VARIABLES DE CONDICION

Page 55: 04.Concurrencia en Java

• Ejemplo: Productor - Consumidor

public Object take() throws InterruptedException {

lock.lock();

try {

while(count== 0)‏ notEmpty.await();

Object x= items[takeptr];

takeptr= (takeptr+1) % items.length;

count= count-1;

notFull.signal();

return x;

} finally {

lock.unlock();

}

}

} //fin de la clase

LOCKS REENTRANTES Y VARIABLES DE CONDICION