04.concurrencia en java
TRANSCRIPT
![Page 2: 04.Concurrencia en Java](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/2.jpg)
DESDE JAVA 1.4
![Page 3: 04.Concurrencia en Java](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/3.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/4.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/5.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/6.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/7.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/8.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/9.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/10.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/11.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/12.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/13.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/14.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/15.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/16.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/17.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/18.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/19.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/20.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/21.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/22.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/23.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/24.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/25.jpg)
DESDE JAVA 1.5
![Page 26: 04.Concurrencia en Java](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/26.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/27.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/28.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/29.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/30.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/31.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/32.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/33.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/34.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/35.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/36.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/37.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/38.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/39.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/40.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/41.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/42.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/43.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/44.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/45.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/46.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/47.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/48.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/49.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/50.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/51.jpg)
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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/52.jpg)
• 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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/53.jpg)
• 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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/54.jpg)
• 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](https://reader036.vdocuments.pub/reader036/viewer/2022081717/544f838daf7959f0338b4577/html5/thumbnails/55.jpg)
• 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