TEMA 6: API Java para Concurrencia de Alto Nivel

Anuncio
TEMA 6: API Java para Concurrencia de
Alto Nivel
CONTENIDO
API Java 5 de Control de la Concurrencia
Clases para Gestión de Variables Atómicas
Clase java.concurrent.util.concurrent.Semaphore
Clase java.concurrent.util.concurrent.CyclicBarrier
El API java.concurrent.util.concurrent.locks y la
interfaz Condition
Colas Contenedoras Sincronizados
Pool 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.
© Antonio Tomeu
API Java para Concurrencia
1 de Alto Nivel
Generalidades
„
„
Disponible a partir de Java 5
Mejoras 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.
© Antonio Tomeu
API Java para Concurrencia
2 de Alto Nivel
¿Qué paquetes usar?
„ Disponible
en los paquetes:
…
java.util.concurrent
… java.util.concurrent.atomic
… java.util.concurrent.locks
„ Cierres
para control de e.m.
„ Semáforos, Barreras
„ Colecciones (clases contenedoras)
concurrentes de acceso sincronizado
„ Variables atómicas
© Antonio Tomeu
API Java para Concurrencia
3 de Alto Nivel
El API java.util.concurrent.atomic
„
„
„
Conjunto de clases que soportan programación
concurrente segura sobre variables simples tratadas
de forma atómica
Clases: AtomicBoolean,AtomicInteger,
AtomicIntegerArray,AtomicLong,AtomicLong
Array,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
4 de Alto Nivel
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 previo
int 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
5 de Alto Nivel
EJERCICIO
„ Escribir
una condición de carrera usando
objetos AtomicInteger y verificar que
se preserva la e.m.
© Antonio Tomeu
API Java para Concurrencia
6 de Alto Nivel
La clase java.util.concurrent.Semaphore
„
„
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 versiones
… avalaiblePermits()
© Antonio Tomeu
API Java para Concurrencia
7 de Alto Nivel
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
8 de Alto Nivel
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.m
s.release();
}
// Thread B
public void run(){
try {
s.acquire();
} catch (InterruptedException ex) { }
//Bloque de código a ejecutar en e.m
s.release();
}
//En el programa principal, hacer siempre
Semaphore s = new Semaphore (1);
9
Ejemplo de Control de E.M. con Semáforos
import java.util.concurrent.*;
public class Tarea_concurrente
extends Thread
{
Semaphore s; //referencia a un semaforo común a otros hilos
public 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");
}
}
} © Antonio Tomeu
API Java para Concurrencia
10 de Alto Nivel
EJERCICIO
„
Descargue la clase
Tarea_Concurrente.java y compile
Descargue la clase
Protocolo_em_semaphore.java,
compile y ejecute.
„ Diseñe, utilizando semáforos, un
código cuyos hilos se
interbloqueen:deadlockSem.java
„
© Antonio Tomeu
API Java para Concurrencia
11 de Alto Nivel
Protocolo de Sincronización Inter-Hilos con
Semáforos
// 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);
© Antonio Tomeu
API Java para Concurrencia
12 de Alto Nivel
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
13 de Alto Nivel
EJERCICIO
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,…
„
© Antonio Tomeu
API Java para Concurrencia
14 de Alto Nivel
La clase
java.util.concurrent.CyclicBarrier
„
„
„
Una barrera es un punto de espera a
partir del cuál todos los hilos se
sincronizan
Ningún hilo pasa la barrera hasta que
todos los hilos esperados llegan a ella
Utilidad
…
…
Unificar resultados parciales
Inicio siguiente fase ejecución simultánea
© Antonio Tomeu
API Java para Concurrencia
15 de Alto Nivel
Protocolo de Barrera
// Codigo de Thread
public 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 barrera
CyclicBarrier Barrera = new CyclicBarrier (numHilos);
new Hilo(Barrera).start();
© Antonio Tomeu
API Java para Concurrencia
16 de Alto Nivel
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
17 de Alto Nivel
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
„
18
El API java.util.concurrent.locks
„
„
Proporciona clases para establecer sincronización
de hilos alternativas a los bloques de código
sincronizado y a los monitores
Clases 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 propios
… Condition: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.
…
© Antonio Tomeu
API Java para Concurrencia
19 de Alto Nivel
El API de la clase ReentrantLock
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
„
© Antonio Tomeu
API Java para Concurrencia
20 de Alto Nivel
Protocolo de Control de E.M. con ReentrantLock
Definir el recurso compartido donde
sea necesario
„ Definir 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()
„
© Antonio Tomeu
API Java para Concurrencia
21 de Alto Nivel
Protocolo de Control de E.M. con
ReentrantLock
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.
© Antonio Tomeu
API Java para Concurrencia
22 de Alto Nivel
EJERCICIOS
„
„
„
„
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.java
Decida que código debe ser sincronizado, y
sincronícelo con cerrojos ReentrantLock. La
nueva clase será Cuenta_Banca_RL.java
Escriba un programa multihebrado que use
objetos de la clase anterior.Llámelo
Usa_Cuenta_Banca_Sync.java
© Antonio Tomeu
API Java para Concurrencia
23 de Alto Nivel
La Interfaz Condition
„
„
„
„
Proporciona variables de condición
Se usa asociada a un cerrojo: siendo
cerrojo una instancia de la clase
ReentrantLock,
cerrojo.newCondition() retorna la
variable.
Operaciones: await(), signal(),
signalAll()
© Antonio Tomeu
API Java para Concurrencia
24 de Alto Nivel
import java.util.concurrent.locks.*;
class buffer_acotado {
final Lock cerrojo = new ReentrantLock();
final Condition noLlena = cerrojo.newCondition();
final Condition noVacia = cerrojo.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, cont;
//declara y crea un cerrojo
//obtiene variables de condicion asociadas a cerrojo
//para control de buffer lleno y vacio
//array de objetos
//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 buffer
return x;
} finally {cerrojo.unlock();}
}
}
© Antonio Tomeu
API Java para Concurrencia
25 de Alto Nivel
EJERCICIOS
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
„
© Antonio Tomeu
API Java para Concurrencia
26 de Alto Nivel
Clases Contenedoras Sincronizadas
„
„
„
A partir de Java 5 se incorporan versiones
sincronizadas de las principales clases
contenedoras
Disponibles en java.util.concurrent
Clases:
ConcurrentHashMap, ConcurrentSkipLis
tMap, ConcurrentSkipListSet, CopyOnW
riteArrayList, y CopyOnWriteArraySet
© Antonio Tomeu
API Java para Concurrencia
27 de Alto Nivel
Colas Sincronizadas
„
„
„
A partir de Java 5 se incorporan diferentes
versiones de colas, todas ellas
sincronizadas
Disponibles en java.util.concurrent
Clases:
LinkedBlockingQueue, ArrayBlockingQu
eue, SynchronousQueue, PriorityBlock
ingQueue,y DelayQueue
© Antonio Tomeu
API Java para Concurrencia
28 de Alto Nivel
Caracterísicas
Son seguras frente a hilos
concurrentes
„ Proporcionan notificación a hilos
según cambia su contenido
„ Son autosincronizadas. Pueden
proveer sincronización (además de
e.m.) por sí mismas
„ Facilitan enormemente la
programación de patrones de
concurrencia como el P-S
„
© Antonio Tomeu
API Java para Concurrencia
29 de Alto Nivel
import java.util.concurrent.*;
public class Productor
implements 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 hueco
System.out.println("Insertando "+x);
}
}
catch(InterruptedException e){}
}
} © Antonio Tomeu
API Java para Concurrencia
30 de Alto Nivel
import java.util.concurrent.*;
public class Consumidor
implements 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
31 de Alto Nivel
EJERCICIOS
„
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 cola
… Active varios productores y un consumidor
… Active un productor y varios consumidores
© Antonio Tomeu
API Java para Concurrencia
32 de Alto Nivel
USO CORRECTO DE COLECCIONES
„
„
„
„
„
Usamos colecciones a través de interfaces
(usabilidad del código)
Usar colecciones no sincronizadas no
mejora mucho el rendimiento
Problemas con esquema tipo productorconsumidor, use colas
Minimice la sincronización explícita
Si con una colección retarda sus
algoritmos, distribuya los datos y use
varias
© Antonio Tomeu
API Java para Concurrencia
33 de Alto Nivel
POOL DE HILOS (Thread Pools)
Hipótesis: Se desea implantar un
servidor web.
„ Enfoques posibles:
„
…
…
Implantación de hilo simple
Implantación multihebrada
© Antonio Tomeu
API Java para Concurrencia
34 de Alto Nivel
PATRÓN DE SERVIDOR WEB DE HILO
SIMPLE
class ServidorWebHiloUnico {
public static void main(String[] args) throws
IOException {
ServerSocket socket = new ServerSocket(80);
//aquí servidor escuchando puerto 80
while (true) {
Socket connection = socket.accept();
procesarSolicitud(connection);
}
}
}
© Antonio Tomeu
API Java para Concurrencia
35 de Alto Nivel
PATRÓN DE SERVIDOR WEB
MULTIHEBRADO
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
}
}
© Antonio Tomeu
API Java para Concurrencia
36 de Alto Nivel
COMPARATIVA
„
„
Servidor de hilo simple es ineficiente (hay
mucha latencia)
Servidor multihebrado mejora la situación
… Procesa
múltiples solicitudes concurrentemente
… Mejora 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.
© Antonio Tomeu
API Java para Concurrencia
37 de Alto Nivel
CONCEPTO DE THREAD POOL
Un pool de hilos es un conjunto de
hilos inactivos a la espera de trabajo
„ Si el programa tienen una tarea que
ejecutar, la pasa al pool, que asigna
un hilo para ejecutarla
„ Una vez acabada, el hilo vuelve a
quedar a la espera de trabajo
„
© Antonio Tomeu
API Java para Concurrencia
38 de Alto Nivel
EL PATRÓN DE THREAD POOL
©
©Antonio
AntonioTomeu
Tomeu
El Lenguaje
API Java
Javapara
y la Concurrencia
Programación
Concurrente
Alto Nivel
39 de
¿Por qué usar un THREAD POOL?
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 pool
„ Permite paralelizar las aplicaciones si
se dispone de varios procesadores
„
© Antonio Tomeu
API Java para Concurrencia
40 de Alto Nivel
La interfaz java.util.concurrent.Executor
„
„
„
„
Un ejecutor es un concepto genérico,
modelado por esta interfaz
Proporcionan 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 ejecutor
Java 5 proporciona dos clases de
ejecutores (implementan la interfaz):
… Ejecutor
de un Thread Pool
… Ejecutor de tareas programadas
© Antonio Tomeu
API Java para Concurrencia
41 de Alto Nivel
La interfaz java.util.concurrent.Executor
package java.util.concurrent;
public interface Executor{
public void execute (Runnable
tarea)
© Antonio Tomeu
API Java para Concurrencia
42 de Alto Nivel
La Clase ThreadPoolExecutor
Sirve para crear un pool de hilos
„ Implementa a ExecutorService
„ El constructor:
„
public ThreadPoolExecutor
(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue);
© Antonio Tomeu
API Java para Concurrencia
43 de Alto Nivel
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
44 de Alto Nivel
import java.util.concurrent.*;
public class pruebaThreadPool {
public static void main(String[] args) {
int
int
nTareas = Integer.parseInt(args[0]);
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
45 de Alto Nivel
UN SERVIDOR DE SOCKETS CON POOL
DE THREADS
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( new
InputStreamReader(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
© Antonio Tomeu
API Java para Concurrencia
46 de Alto Nivel
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
47 de Alto Nivel
EJERCICIOS
Descargar y compilar Tarea.java y
pruebaThreadPool.java
„ Abrir el administrador de tareas
„ Efectuar las siguientes pruebas:
„
… java
pruebaThreadPool n m
… Para 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
© Antonio Tomeu
API Java para Concurrencia
48 de Alto Nivel
Descargar