Diapositivas del Tema 6

Preview:

Citation preview

1

TEMA 6: API Java para Concurrencia de Alto Nivel

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

CONTENIDOAPI Java 5 de Control de la ConcurrenciaClases para Gestión de Variables AtómicasClase java.concurrent.util.concurrent.SemaphoreClase java.concurrent.util.concurrent.CyclicBarrierEl API java.concurrent.util.concurrent.locks y la interfaz ConditionColas Contenedoras SincronizadosPool de Threads:

Concepto, utilidad y utilización

BIBLIOGRAFÍA RECOMENDADA:[Göe06] Göetz et al. Java Concurrency in Practice, 2006.[Oaks04] Oaks & Wong. Java Threads. O`Reilly, 2004.

2

Disponible a partir de Java 5Mejoras respecto a los mecanismos previos de control de la concurrencia• Generan menos sobrecarga en tiempo de ejecución• Permiten controlar la e.m. y la sincronización a un

nivel más fino• Soporta primitivas clásicas como los semáforos o las

variables de condición en monitores• Añade nuevas primitivas y gestión de hilos mediante

pools.

Generalidades

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

3

Disponible en los paquetes:java.util.concurrentjava.util.concurrent.atomicjava.util.concurrent.locks

Cierres para control de e.m.Semáforos, BarrerasColecciones (clases contenedoras) concurrentes de acceso sincronizadoVariables atómicas

¿Qué paquetes usar?

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

4

El API java.util.concurrent.atomic

Conjunto de clases que soportan programación concurrente segura sobre variables simples tratadas de forma atómicaClases: AtomicBoolean,AtomicInteger, AtomicIntegerArray,AtomicLong,AtomicLongArray,AtomicReference<V>, etc.OJO: No todos los métodos del API de estas clases soportan concurrencia segura. Sólo aquellos en los que se indica que la operación se realiza de forma atómica

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

5

import java.util.concurrent.atomic.*;

class Hilo extends Thread{

AtomicInteger cont = null; //el contador es entero “atómico”

public Hilo(AtomicInteger c) {this.cont = c;}public void run(){//se incrementa cont, retornando el valor previoint valor = this.cont.incrementAndGet();System.out.println(valor);}

}

public class prueba_var_atomic {

public static void main(String[] args) {

AtomicInteger cnt = new AtomicInteger(0);

for(int i=0; i<100; i++){Hilo h = new Hilo(cnt);h.start();

}}

} © Antonio Tomeu API Java para Concurrencia de Alto Nivel

6

Escribir una condición de carrera usando objetos AtomicInteger y verificar que se preserva la e.m.

EJERCICIO

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

7

Permite disponer de semáforos de conteo de semántica mínima igual a la estándar de Djikstra, o aumentada.Métodos principales:Semaphore (long permits)acquire(), acquire(int permits)release(), release (int permits)tryAcquire(); varias versionesavalaiblePermits()

La clase java.util.concurrent.Semaphore

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

8

import java.util.concurrent.*;

public class Bloqueo_Semaforo {

public static void main(String[] args)throws InterruptedException{Semaphore sem = new Semaphore (2);sem.acquire(2);System.out.println("Semaforo actualizado a valor 0...");System.out.println("y su estado es: "+sem.toString());System.out.println("Ahora intentamos adquirirlo...");sem.tryAcquire();System.out.println("sin bloqueo por no conseguirlo");System.out.println("Ahora intentamos adquirirlo...");sem.tryAcquire(3L, TimeUnit.SECONDS);System.out.println("tras esperar lo indicado sin consguirlo...");sem.acquire();System.out.println("Aquí no llegaremos nunca...");

}}

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

9

Protocolo de Control de E.M. con Semáforos// Thread A public void run(){try {

s.acquire();} catch (InterruptedException ex) { }//Bloque de código a ejecutar en e.ms.release();

}

// Thread B public void run(){try {

s.acquire();} catch (InterruptedException ex) { }//Bloque de código a ejecutar en e.ms.release();

}

//En el programa principal, hacer siempre Semaphore s = new Semaphore (1);

10

import java.util.concurrent.*;

public class Tarea_concurrenteextends Thread

{Semaphore s; //referencia a un semaforo común a otros hilospublic Tarea_concurrente(Semaphore param)

{s = param;}

public void run (){

for(;;){try {s.acquire();} catch (InterruptedException e) {}System.out.println("Hilo "+this.getName()+" entrando a

seccion critica");s.release();System.out.println("Hilo "+this.getName()+" saliendo de

seccion critica");}

}}

Ejemplo de Control de E.M. con Semáforos

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

11

Descargue la clase Tarea_Concurrente.java y compileDescargue la clase Protocolo_em_semaphore.java, compile y ejecute.Diseñe, utilizando semáforos, un código cuyos hilos se interbloqueen:deadlockSem.java

EJERCICIO

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

12

// Thread A public void esperarSeñal(semaphore s) {try {

s.acquire();} catch (InterruptedException ex) { }

}

// Thread B public void enviarSeñal(Semaphore s) {

s.release(); }

//En el programa principal, hacer siempre Semaphore s = new Semaphore (0);

Protocolo de Sincronización Inter-Hilos con Semáforos

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

13

import java.util.concurrent.*;class HiloReceptor extends Thread{

Semaphore sem;public HiloReceptor(Semaphore s){sem = s;}public void run(){System.out.println("Hilo Receptor esta esperando la senal");try{sem.acquire();} catch (InterruptedException e) {}System.out.println("Hilo Receptor ha recibido la senal");

}}

class HiloSenalador extends Thread{ Semaphore sem;

public HiloSenalador(Semaphore s){sem = s;}public void run(){sem.release();System.out.println("Hilo Senalador enviando senal...");

}}public class protocolo_sincronizacion_semaphore {

public static void main(String[] args){ int v_inic_sem = 0;Semaphore s = new Semaphore(v_inic_sem);new HiloReceptor(s).start();new HiloSenalador (s).start();

}} © Antonio Tomeu API Java para Concurrencia de Alto Nivel

14

Utilizando las semáforos que sean necesarios, provea la sincronización por turno cíclico de tres hilos diferentes. Nombre sus ficheros HA.java, HB.java,…Implante ahora una lector-escritor con semáforos. Nombre sus ficheros LE1.java, LE2.java,…

EJERCICIO

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

15

Una barrera es un punto de espera a partir del cuál todos los hilos se sincronizanNingún hilo pasa la barrera hasta que todos los hilos esperados llegan a ellaUtilidad

Unificar resultados parcialesInicio siguiente fase ejecución simultánea

La clase java.util.concurrent.CyclicBarrier

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

16

// Codigo de Threadpublic Hilo extends Thread {P ublic Hilo (CyclicBarrier bar){…}public void run() {try {int i = bar.await();}

catch (BrokenBarrierException e) {}catch (InterruptedException e) {}

//codigo a ejecutar cuando se abre barrera…}

}}

//En el programa principal, hacer siempre int numHilos = n; //numero de hilo que abren barreraCyclicBarrier Barrera = new CyclicBarrier (numHilos);new Hilo(Barrera).start();

Protocolo de Barrera

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

17

import java.util.concurrent.*;

class Hilo extends Thread{CyclicBarrier barrera = null;

public Hilo (CyclicBarrier bar){barrera = bar;}

public void run(){try {int i = barrera.await();}

catch (BrokenBarrierException e) {}catch (InterruptedException e) {}

System.out.println("El hilo "+this.toString()+" paso la barrera...");

}}

public class UsaBarreras {public static void main(String[] args) {int numHilos = 3;

CyclicBarrier PasoANivel = new CyclicBarrier (numHilos);new Hilo(PasoANivel).start();

}}© Antonio Tomeu API Java para Concurrencia de Alto Nivel

18

EJERCICIO

Un vector de 100 enteros es escalado mediante f(v[i])=v[i]2.

Posteriomente el vector se suma entero en un único número.Escriba un programa en java multihebrado (utilizando barreras) que realice la tarea: vectSum.java

19

Proporciona clases para establecer sincronización de hilos alternativas a los bloques de código sincronizado y a los monitoresClases e interfaces de interés:

ReentrantLock: proprociona cerrojos de e.m. de semántica equivalente a synchronized, pero con un manejo más sencillo.LockSupport: proporciona primitivas de bloqueo de hilos que permiten al programador diseñar sus clases de sincronización y cerrojos propiosCondition:es una interface, cuyas instancias se usan asociadas a locks. Implementan variables de condición y proporcionan una alternativa de sincronización a los métodos wait, notify y notifyAll de la clase Object.

El API java.util.concurrent.locks

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

20

Proporciona cerrojos reentrantes de semántica equivalente a synchronized.Métodos lock()-unlock()Método isLocked()Método Condition newCondition() que retorna una variable de condición asociada al cerrojo

El API de la clase ReentrantLock

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

21

Definir el recurso compartido donde sea necesarioDefinir un objeto c de clase ReentrantLock para control de la exclusión mutua.Acotar la sección crítica con el par c.lock() y c.unlock()

Protocolo de Control de E.M. con ReentrantLock

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

22

class Usuario { private final ReentrantLock cerrojo = new ReentrantLock();

// ... public void metodo() { cerrojo.lock();try { // ... cuerpo del metodo en e.m. }

finally {cerrojo.unlock() } }

}

NOTA: Semántica equivalente a haber etiquetado el segmento de código crítico como synchronized. Útil para adaptar código concurrente antiguo a java 5.

Protocolo de Control de E.M. con ReentrantLock

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

23

Escribir un protocolo de e.m. con cerrojos ReentrantLock y guardarlo en eMRL.java. Escribir una clase que lo use.Descargue otra vez nuestra vieja clase Cuenta_Banca.javaDecida que código debe ser sincronizado, y sincronícelo con cerrojos ReentrantLock. La nueva clase será Cuenta_Banca_RL.javaEscriba un programa multihebrado que use objetos de la clase anterior.LlámeloUsa_Cuenta_Banca_Sync.java

EJERCICIOS

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

24

Proporciona variables de condiciónSe usa asociada a un cerrojo: siendo cerrojo una instancia de la clase ReentrantLock,cerrojo.newCondition() retorna la variable.Operaciones: await(), signal(), signalAll()

La Interfaz Condition

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

25

import java.util.concurrent.locks.*;

class buffer_acotado {final Lock cerrojo = new ReentrantLock(); //declara y crea un cerrojofinal Condition noLlena = cerrojo.newCondition(); //obtiene variables de condicion asociadas a cerrojofinal Condition noVacia = cerrojo.newCondition(); //para control de buffer lleno y vacio

final Object[] items = new Object[100]; //array de objetosint putptr, takeptr, cont; //punteros de insercion y extracción

public void put(Object x) throws InterruptedException {cerrojo.lock();try {while (cont == items.length)noLlena.await(); //duerme a hilo productor si buffer lleno

items[putptr] = x;if (++putptr == items.length) putptr = 0;++cont;noVacia.signal(); //despierta a hilo consumidor esperando presencia de datos en buffer

} finally {cerrojo.unlock();}}

public Object take() throws InterruptedException {cerrojo.lock();try {while (cont == 0)noVacia.await(); //duerme a hilo consumidor si buffer vacio

Object x = items[takeptr];if (++takeptr == items.length) takeptr = 0;--cont;noLlena.signal(); //despierta a hilo productor esperando presencia de huecos en bufferreturn x;

} finally {cerrojo.unlock();}}

}

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

26

Descargue y compile la clase buffer_acotado.java¿Qué diferencias tiene con la clase Buffer.java? (carpeta código Tema5)Implemente ahora hilos productores y consumidores que usen el buffer acotado y láncelos. Llame a su código prod_con_condition.java

EJERCICIOS

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

27

A partir de Java 5 se incorporan versiones sincronizadas de las principales clases contenedorasDisponibles en java.util.concurrentClases: ConcurrentHashMap, ConcurrentSkipListMap, ConcurrentSkipListSet, CopyOnWriteArrayList, y CopyOnWriteArraySet

Clases Contenedoras Sincronizadas

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

28

A partir de Java 5 se incorporan diferentes versiones de colas, todas ellas sincronizadasDisponibles en java.util.concurrentClases: LinkedBlockingQueue, ArrayBlockingQueue, SynchronousQueue, PriorityBlockingQueue,y DelayQueue

Colas Sincronizadas

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

29

Son seguras frente a hilos concurrentesProporcionan notificación a hilos según cambia su contenido Son autosincronizadas. Pueden proveer sincronización (además de e.m.) por sí mismasFacilitan enormemente la programación de patrones de concurrencia como el P-S

Caracterísicas

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

30

import java.util.concurrent.*;

public class Productorimplements Runnable

{

LinkedBlockingQueue<Integer> data;Thread hilo;

public Productor(LinkedBlockingQueue<Integer> l){

this.data = l;hilo = new Thread (this);hilo.start();

}

public void run(){try {for(int x=0;;x++){data.put(new Integer(x)); //bloqueo de hilo si no hay huecoSystem.out.println("Insertando "+x);

}}

catch(InterruptedException e){}}

} © Antonio Tomeu API Java para Concurrencia de Alto Nivel

31

import java.util.concurrent.*;

public class Consumidorimplements Runnable

{

LinkedBlockingQueue<Integer> data;Thread hilo;

public Consumidor(LinkedBlockingQueue<Integer> l){

this.data = l;hilo = new Thread (this);hilo.start();

}

public void run(){try {for(;;) //bloquea a hilo consumidor si no hay datos…

System.out.println("Extrayendo "+data.take().intValue());}

catch(InterruptedException e){}}

}

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

32

Descargue (subcarpeta colecciones seguras) Productor.java, Consumidor.java y ProdConColaBloqueante.java

Ejecute. ¿Cómo explica el comportamiento observado?Modificaciones:

Aumente el número de ranuras de la colaActive varios productores y un consumidorActive un productor y varios consumidores

EJERCICIOS

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

33

Usamos colecciones a través de interfaces (usabilidad del código)Usar colecciones no sincronizadas no mejora mucho el rendimientoProblemas con esquema tipo productor-consumidor, use colasMinimice la sincronización explícitaSi con una colección retarda sus algoritmos, distribuya los datos y use varias

USO CORRECTO DE COLECCIONES

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

34

Hipótesis: Se desea implantar un servidor web.Enfoques posibles:

Implantación de hilo simpleImplantación multihebrada

POOL DE HILOS (Thread Pools)

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

35

class ServidorWebHiloUnico {public static void main(String[] args) throwsIOException {

ServerSocket socket = new ServerSocket(80);//aquí servidor escuchando puerto 80

while (true) {Socket connection = socket.accept();procesarSolicitud(connection);

}}

}

PATRÓN DE SERVIDOR WEB DE HILO SIMPLE

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

36

class ServidorWebMultiHebrado {public static void main(String[] args) throws IOException {ServerSocket socket = new ServerSocket(80);//aquí servidor a la escucha en puerto 80//un hilo por solicitud

while (true) {final Socket connection = socket.accept();Runnable task = new Runnable() {public void run() {procesarSolicitud(connection);

}};new Thread(task).start();}//while

}}

PATRÓN DE SERVIDOR WEB MULTIHEBRADO

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

37

Servidor de hilo simple es ineficiente (hay mucha latencia)Servidor multihebrado mejora la situación

Procesa múltiples solicitudes concurrentementeMejora el rendimiento general

Pero no está exento de problemas:Consumo de recursos (al crear los hilos)Estabilidad (número de peticiones elevado)

Alternativa: Utilizar un pool de hilos y un ejecutor.

COMPARATIVA

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

38

Un pool de hilos es un conjunto de hilos inactivos a la espera de trabajoSi el programa tienen una tarea que ejecutar, la pasa al pool, que asigna un hilo para ejecutarlaUna vez acabada, el hilo vuelve a quedar a la espera de trabajo

CONCEPTO DE THREAD POOL

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

39

EL PATRÓN DE THREAD POOL

© Antonio Tomeu El Lenguaje Java y la Programación Concurrente© Antonio Tomeu API Java para Concurrencia de Alto Nivel

40

Crear un hilo es costoso (latencias). Creando un pool, creamos los hilos una vez y los reutilizamos varias.Permite un mejor diseño de programas, delegando la gestión del ciclo de vida de los hilos al poolPermite paralelizar las aplicaciones si se dispone de varios procesadores

¿Por qué usar un THREAD POOL?

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

41

Un ejecutor es un concepto genérico, modelado por esta interfazProporcionan un patrón de diseño para programas multihebrados, modelándolos como una serie de tareas.Abstracción de los hilos; se crea una tarea, y se pasa al ejecutorJava 5 proporciona dos clases de ejecutores (implementan la interfaz):

Ejecutor de un Thread PoolEjecutor de tareas programadas

La interfaz java.util.concurrent.Executor

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

42

package java.util.concurrent;

public interface Executor{

public void execute (Runnabletarea)

La interfaz java.util.concurrent.Executor

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

43

La Clase ThreadPoolExecutor

Sirve para crear un pool de hilosImplementa a ExecutorServiceEl constructor:

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue<Runnable> workQueue); © Antonio Tomeu API Java para Concurrencia de Alto Nivel

44

public class Tarea implements Runnable {

int numTarea;

public Tarea(int n) {numTarea = n;}

public void run()

{for (int i=1; i<100; i++){ System.out.println("Esta es la tarea numero: "+numTarea+ "

imprimiendo "+i);}

}

}

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

45

import java.util.concurrent.*;

public class pruebaThreadPool {public static void main(String[] args) {

int nTareas = Integer.parseInt(args[0]);int tamPool = Integer.parseInt(args[1]);

ThreadPoolExecutor miPool = new ThreadPoolExecutor(tamPool, tamPool, 60000L,TimeUnit.MILLISECONDS,

new LinkedBlockingQueue<Runnable>());

Tarea[] tareas = new Tarea [nTareas];for (int i=0; i<nTareas; i++){

tareas[i] = new Tarea(i);miPool.execute(tareas[i]);

}miPool.shutdown();

}}

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

46

import java.net.*;import java.io.*;import java.util.concurrent.*;

public class SocketWebServerThreadPool implements Runnable {Socket enchufe;

public SocketWebServerThreadPool(Socket s){enchufe = s;}

public void run() {try{ BufferedReader entrada = new BufferedReader( newInputStreamReader(enchufe.getInputStream()));

String datos = entrada.readLine();int j; int i = Integer.valueOf(datos).intValue();for(j=1; j<=20; j++) System.out.println("Una tarea de servicio

escribiendo el dato "+i);enchufe.close();System.out.println("La tarea de servicio cierra su conexion...");} catch(Exception e) {System.out.println("Error...");}

}//run

UN SERVIDOR DE SOCKETS CON POOL DE THREADS

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

47

public static void main (String[] args) {int i; int puerto = 2001;int nTareas = Integer.parseInt(args[0]);int tamPool = Integer.parseInt(args[1]);

ThreadPoolExecutor miPool = new ThreadPoolExecutor(tamPool, tamPool, 60000L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());

try{ ServerSocket chuff = new ServerSocket(puerto, 3000);while (true){

System.out.println("Esperando solicitud de conexion...");Socket cable = chuff.accept();System.out.println("Recibida solicitud de conexion...");

miPool.execute(new SocketWebServerThreadPool(cable));}//while

} catch (Exception e) {System.out.println("Error en sockets...");}}//main

}

© Antonio Tomeu API Java para Concurrencia de Alto Nivel

48

Descargar y compilar Tarea.java y pruebaThreadPool.javaAbrir el administrador de tareasEfectuar las siguientes pruebas:java pruebaThreadPool n mPara n=200 y m creciente

Implante ahora un protocolo productor-consumidor con un único buffer, y un pool de 8 tareas productoras y 6 consumidoras

EJERCICIOS

© Antonio Tomeu API Java para Concurrencia de Alto Nivel