Documente Academic
Documente Profesional
Documente Cultură
Concurrencia en Java
Luis Gajardo lgajardo@ubiobio.cl
HEBRAS EN JAVA
Creacin de hebras Especificar el comportamiento de una hebra: Escribir la clase A que implementa la interfaz Runnable y redefinir el mtodo run()
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 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) ...
PATRONES DE LA CONCURRENCIA
Patrn 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
Patrn Futuros (continuacin)
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
Patrn Futuros: Generalizacin
class FutureComputation implements Runnable { declarar parmetros tipo-retorno res; Thread t; FutureComputation(parmetros) { asignar parmetros 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
Patrn Futuros: Ejercicio propuesto factorial Implementar el mtodo: 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 ... } }
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 instruccin especial:
synchronized ( expresin ) { /*cuerpo*/ }
MONITORES EN JAVA
Semntica de la instruccin synchronized Una hebra T ejecuta la instruccin synchronized de la siguiente manera: T evala expresin. Sea M el monitor referenciado por la expresin. T solicita el monitor M. T podra tener que esperar, debido a otras hebras compitiendo por M. T ejecuta el cuerpo de la instruccin synchronized. T devuelve el monitor M. T posee el monitor M mientras se ejecuta el cuerpo o cualquier mtodo invocado desde el cuerpo Java garantiza que M siempre se devuelve cuando se sale de la instruccin synchronized M es otorgado en orden de llegada
MONITORES EN JAVA
Devolucin temporal del monitor
En ocasiones una operacin no puede ejecutarse mientras no se verifique una condicin.
MONITORES EN JAVA
Reactivacin 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; } } }
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; } } }
No use notify
notify() retoma una sola hebra, no sabemos cul. Es muy difcil ver la condicin de borde que har que su solucin no funcione, pero existe. notifyAll() es ineficiente pero no se puede evitar. Es un defecto de diseo de Java Los monitores de Java se inspiran de las regiones crticas 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
synchronized void take(int k) { controlador try { while (state[k]) public void run() { wait(); for (;;) { state[k]= true; int first= Math.min(i, (i+1)%5); } int last= Math.max(i, (i+1)%5); catch(InterruptedException e){ ctrl.take(first); } ctrl.take(last); } eat(i, (i+1)%5); synchronized void leave(int k) { ctrl.leave(i); state[k]= false; ctrl.leave((i+1)%5); notifyAll(); think(); } } } } Sea consiente que en esta solucin los filsofos pueden sufrir hambruna
monitor
FRAMEWORK EXECUTOR
Un Executor es un objeto que ejecuta tareas de tipo Runnable.
Es similar a la invocacin:
new Thread(aRunnableObject).start();
Dos observaciones: La creacin de threads puede ser costosa Un mecanismo ptimo para la mantencin de threads es difcil de implementar. Solucin: El nuevo framework Executor resuelve todos estos problemas separando la utilizacin del thread de la forma en cmo este debe ser creado. Adems permite estandarizar la invocacin, planificacin, ejecucin y control de tareas asncronas segn un conjunto de polticas de ejecucin.
FRAMEWORK EXECUTOR
Creando un Executor
Crearlo usando un mtodo 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 nmero de threads indicado; dichos threads siempre estarn listos para procesar tareas. El pool maneja tambin una cola de tareas; cada thread toma una tarea de la cola y la procesa, al terminar contina con otra tarea de la cola hasta que no queden ms. Otros executors ScheduledThreadPool, SingleThreadExecutor()...
FRAMEWORK EXECUTOR
Usando un Executor
Por ejemplo:
Executor executor = Executors.newFixedThreadPool(5); executor.execute (new RunnableTask1()); executor.execute (new RunnableTask2());
FRAMEWORK EXECUTOR
Ejemplo: un simple pool de threads (conjunto de hilos) con un tamao por
defecto de 2 y un mximo de 4 hilos. El mtodo shutdown() termina todos los hilos si no hay tareas en ejecucin. Por lo tanto, Executor libera al programador de la gestin 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 coleccin es un objeto que agrupa mltiples elementos en una sola unidad.
Las colecciones se usan para almacenar, recuperar, manipular y comunicar agregacin de datos. Normalmente representan tem de datos que forma un grupo natural, tal y como una baraja de cartas, carpeta de correos o directorio telefnico. Collection es la interface genrica en java que permite realizar esta agrupacin. Java 1.4 posee variadas colecciones, pero no pueden ser utilizadas en entornos concurrentes a no ser que el programador se preocupe de la sincronizacin.
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 lmite 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 lmite opcional de capacidad.
PriorityBlockingQueue: cola de prioridad que ordena los elementos automticamente segn prioridad.
SynchronousQueue: cola sincronizada, slo realiza la operacin cuando es posible, sino espera.
COLECCIONES CONCURRENTES
Los mtodos declarados en la interfaz BlockingQueue: Para aadir elementos add() : agrega un elemento offer() : devuelve false si est llena, no genera excepcin put() : se bloquea si la cola est llena Para extraer elementos remove() : extrae el elemento del principio. Genera excepcin si vaco poll() : igual que remove() pero si la cola est vaca devuelve null take() : se bloquea si la cola est vaca drainTo() : vaca la cola y devuelve coleccin Para consultar sin extraer element() : devuelve, sin extraerlo, el primer elemento. Excep. si vaca peek() : igual que element() pero devuelve null si cola vaca
COLECCIONES CONCURRENTES
Ejemplo:
Implementacin del problema del productor - consumidor
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) {...} }
COLECCIONES CONCURRENTES
class Ejemplo { void main() { //implementacin especfica 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 atmicamente variables individuales (tipos primitivos o referenciados) permitiendo el uso de mtodos para aritmtica atmica y asignacin 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() { c.incrementAndGet(); } } public int value() { return c.get(); } public void decrement() { c.decrementAndGet(); }
Semaphore semforos
Locks countDownLatch CyclicBarrier Exchanger
SEMAPHORE
Posee el mismo funcionamiento que los semforos tradicionales, permite controlar el nmero de procesos que acceden a la seccin crtica. Crear el semforo: Semaphore(int tickets)
Crea el semforo con el nmero de tickets indicado.
(java.util.concurrent.Semaphore)
acquire()
Saca un ticket del semforo si hay disponibles, sino bloquea el thread hasta que exista uno disponible. Lanza la excepcin InterruptedException.
release()
Aporta un ticket al semforo, 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(); Solicita un ticket // SECCION CRITICA semaphore.release(); } catch (InterruptedException e) {} Devuelve un ticket } public static void main(String[] args) { Example e1= new Example(1); Example e2= new Example(2); e1.start(); e2.start(); }}
SEMAPHORE
Otros mtodos: tryAcquire()
Este mtodo 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).
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 tambin se puede lanzar un nuevo thread que realice alguna tarea (mediante la interfaz Runnable). Dado que son cclicas pueden ser utilizadas varias veces (varios puntos de espera).
CYCLIC BARRIER
Los mtodos 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 adems ejecuta una accin 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 mximo (timeout). El parmetro unit indica la unidad en la cual se ha expresado timeout.
CYCLIC BARRIER
Ejemplo:
int THREADS = 5; CyclicBarrier barrier = new CyclicBarrier(THREADS); ... barrier.await(); //seccin crtica, continua solo cuando 5 threads invoquen await() barrier.await(); //otra seccin crtica
LOCKS REENTRANTES
(java.util.concurrent.locks.ReentrantLock) Se introduce un mecanismo de sincronizacin alternativo al lock normal que se define a travs de la clase ReentrantLock y cuya funcionalidad se define a travs 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 intrnsecos deben ser liberados en el mismo bloque de cdigo en el cual se suspendi.
LOCKS REENTRANTES
Comparacin: - El Lock normal conduce a un estilo de programacin sencillo, seguro y compatible con la gestin de excepciones. - El ReentrantLock conduce a estrategias menos seguras, pero ms flexibles, proporciona mayor vivacidad y mejores caractersticas de respuesta.
LOCKS REENTRANTES
La interface Lock provee varios mtodos 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.
LOCKS REENTRANTES
A diferencia del lock normal, la interface Lock ofrece la toma de un lock: incondicional, no bloqueante, temporizado o interrumpible. Adems todas las operaciones de suspensin y liberacin de un lock son explcitas.
si es necesario
La liberacin debe hacerse en una sentencia finally, ya que hay que preveer la posibilidad de una excepcin, y en este caso el lock debe de liberarse explcitamente tambin.
boolean await(long time, TimeUnit unit); // causa la espera del thread, por el tiempo especificado, sobre // una variable de condicin. No se bloquea. Retorna false cuando // no se bloquea.
void signal(); // Notifica una condicin cumplida, despierta solo un thread void signalAll(); // Notifica una condicin cumplida. Despierta a todos los threads ...