Date post: | 28-Oct-2014 |
Category: |
Documents |
Upload: | xxxtunoxxx |
View: | 225 times |
Download: | 1 times |
DESDE JAVA 1.4
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();
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);
}
}
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);
}
}
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) ...
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
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; }
}
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);
}
}
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
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();
}
}
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; }
} }
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) { ... }
} }
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;
}
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();
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.
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
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
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;
}
}
}
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
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;
} } }
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 ...;
}
}
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
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
DESDE JAVA 1.5
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.
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.
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()...
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()
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);
}
}
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();
}
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());
}
}
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.
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.
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
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
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();
}
}
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();
}
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
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.
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
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();
}
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).
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.
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
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)
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.
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
}
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.
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.
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
• 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
• Ejemplo: Productor - Consumidor
public Object take() throws InterruptedException {
...
try {
...
} finally {
...
}
}
} //fin de la clase
LOCKS REENTRANTES Y VARIABLES DE CONDICION
• 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
• 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