Download Procesamiento paralelo con hilos de Java

Document related concepts
no text concepts found
Transcript
CCP
Procesamiento paralelo con hilos de
Java
Tabla de contenidos
1. Soporte para hilos en Java ...................................................................................................... 1
2. Creación y control de un hilo .................................................................................................. 2
2.1. Métodos de creación de un hilo ..................................................................................... 2
2.2. Control de hilos mediante métodos de Thread .................................................................. 2
3. Sincronización por exclusión mutua ......................................................................................... 2
3.1. Sincronización de métodos ........................................................................................... 2
3.2. Sincronización de bloques ............................................................................................ 3
3.3. Sincronización de métodos y bloques estáticos ................................................................. 3
4. Espera y notificación de eventos .............................................................................................. 3
4.1. Wait y notify ............................................................................................................. 3
4.2. NotifyAll .................................................................................................................. 4
4.3. Wait y sleep .............................................................................................................. 4
4.4. Wait y notify en métodos estáticos ................................................................................. 4
5. La integral numérica con hilos ................................................................................................. 4
5.1. Diseño ..................................................................................................................... 4
5.2. La clase NumIntegralTh .............................................................................................. 4
5.3. La clase IntervalTh ..................................................................................................... 5
5.4. La clase IntegralRes .................................................................................................... 6
5.5. Versión en C ............................................................................................................. 6
5.6. Tiempos de ejecución .................................................................................................. 7
Bibliografía .............................................................................................................................7
1. Soporte para hilos en Java
En Java el soporte a la concurrencia (paralelismo) basado en hilos se hace en el propio lenguaje y no en base a
una librería auxiliar (p.ej. PThreads, hilos win32).
El soporte de Java a los hilos se hace mediante los siguientes elementos:
•
Dotando a la clase ancestral Object de los métodos Wait(), Notify() y NotifyAll() que dan soporte a los
eventos
•
Asociando un lock a cada clase y a cada objeto, la obtención y la devolución del lock se hace mediante la
utilización de la palabra reservada synchronized mediante la cual se definen métodos o bloques de código a
los que se accede en exclusión mutua
•
Proporcionando la clase Thread en la librería java.lang que implementa los mecanismos básicos para realizar
hilos
•
La interfaz Runnable permite la creación de hilos en clases que extienden otra clase diferente de Thread
•
Otras funcionalidades disponibles son:
•
El control de prioridad
•
La posibilidad de limitar el número de hilos activos mediante la clase ThreadPool
•
La posibilidad de controlar grupos de hilos mediante la clase ThreadGroup
IV-A.1
Procesamiento paralelo con hilos de Java
CCP
2. Creación y control de un hilo
2.1. Métodos de creación de un hilo
En Java se puede crear un hilo mediante dos mecanismos:
•
Extendiendo la clase Thread
•
Implementado la interfaz Runnable
Tanto Thread como Runnable forman parte de java.lang con lo que no requieren ser importados explícitamente.
Ambos métodos tienen en común el procedimiento básico que consiste en implementar el método run(). El método para iniciar el hilo depende de la forma como se halla creado:
•
Extendiendo la clase Thread Se llama al método start de los objetos que instancian la clase
•
Implementando Runnable Se crea un objeto Thread con el constructor que tiene como parámetro un objeto
Runnable y llamando posteriormente al método start de dicho objeto
En las páginas 15-23 se dispone de un ejemplo en el que se incluye una animación en un Applet utilizando los
dos mecanismos: una clase auxiliar que extiende Thread e implementando Runnable en el propio Applet.
2.2. Control de hilos mediante métodos de Thread
La clase Thread contiene una serie de métodos que permiten controlar el funcionamiento de los hilos:
•
isAlive Devuelve un valor true si el hilo esta activo, es decir su método run ha sido iniciado con start y todavía no ha finalizado
•
join Condiciona la continuación del hilo actual a la terminación de otro hilo, la duración de esta espera se
puede limitar
•
setName y getName Permiten dar un nombre a un hilo y obtener dicho nombre, respectivamente. Esto tiene
interés sobre todo para la depuración. La función setName se puede evitar utilizando los constructores adecuados de Thread (pag 30)
También se dispone de los siguientes métodos estáticos:
•
sleep Detiene la ejecución del hilo que hace la llamada durante el tiempo que se especifica
•
currentThread Permite identificar al hilo que hace la llamada (ejemplo en la página 31)
•
enumerate Devuelve una matriz con referencias a todos los hilos creados por el programa (no necesariamente activos) y su número
•
activeCount Devuelve el número de hilos creados por el programa (no necesariamente activos)
3. Sincronización por exclusión mutua
3.1. Sincronización de métodos
La ejecución de métodos de un objeto se puede forzar a que se haga en exclusión mutua utilizando la palabra reIV-A.2
CCP
Procesamiento paralelo con hilos de Java
servada synchronized en la declaración del método (pag 45)
Este mecanismo asegura que la modificación del estado del objeto se realiza de forma consistente evitando las
condiciones de carrera ya que solamente un objeto puede estar ejecutando en un momento dado un método sincronizado, o sea que no puede haber simultáneamente dos o mas objetos ejecutando métodos sincronizados (el
mismo método o diferentes métodos) de forma simultánea.
Esta serialización del acceso a los métodos sincronizados asegura la evitación de condiciones de carrera, aunque
por otra parte reduce las posibilidades de paralelismo en la utilización del objeto.
El funcionamiento de esto consiste en que al iniciar un método sincronizado se toma el lock asociado al objeto y
al finalizar se devuelve el lock dejándolo disponible.
3.2. Sincronización de bloques
Con el fin de reducir el efecto de la serialización de código sincronizado sobre el paralelismo, se puede hacer
uso de la sincronización de bloque que resulta más flexible:
•
Permite serializar segmentos de código dentro de un método
•
Permite elegir el objeto cuyo lock se va a utilizar en la sincronización (no ha de ser necesariamente this)
Un ejemplo de sincronización de un bloque aparece la página 55.
syncrhonized(obj) {
...
...
}
Hay que evitar que el objeto cuyo lock se utiliza en synchonized sea null, y también tener cuidado ya que las referencias a objetos pueden cambiar en tiempo de ejecución y por tanto los efectos sobre la sincronización pueden generar errores.
3.3. Sincronización de métodos y bloques estáticos
Los métodos estáticos también pueden sincronizarse ya que cada clase tiene asociado también un lock, que en
realidad es el lock del objeto de tipo Class asociado a la clase y que puede obtenerse explícitamente mediante:
Class classobj = Class.forName("Nombre_de_la_clase");
Los bloques de código dentro de un método estático pueden sincronizarse haciendo uso del objeto mencionado:
public class Ejemplo {
void proceso() {
....
synchronized (Class.forName("Ejemplo")) {
.....
}
....
}
}
4. Espera y notificación de eventos
4.1. Wait y notify
El mecanismo de espera y notificación viene proporcionado en Java en la clase ancestra Object, este mecanismo
evita la espera ocupada de un hilo respecto de un cambio que ha de producir otro hilo. El primero hace una espera no ocupada (wait) y el segundo una notificación (notify).
IV-A.3
Procesamiento paralelo con hilos de Java
CCP
Ambos métodos han de llamarse desde código sincronizado con el mismo lock para asegurar que el test de la
condición y la entrada en espera se realizan en exclusión mutua. La función wait() libera el lock antes de iniciar
la espera y retoma el lock después de recibir la notificación.
En las páginas 67-68 se tiene una versión de un semáforo con espera ocupada para la operación get. La versión
con wait de la página 70 evita la espera ocupada y requiere sincronizar el método. Obsérvese que wait está en un
bucle en el que después de salir de wait se vuelve a testear la condición.
4.2. NotifyAll
Cuando hay varios hilos en espera de la misma condición no es posible saber cual de ellos recibirá la notificación.
El método NotifyAll permite notificar a todos los hilos en espera que han utilizado el mismo lock, será el mismo
lock que se ha utilizado para sincronizar el código que incluye la llamada a NotifyAll.
En la página 75 se tiene la clase ResourceThrottle como ejemplo de utilización de NotifyAll. Todos los hilos
son notificados pero sólo uno de los que demandan un número igual o menor de los recursos disponibles saldrá
de la espera.
En las páginas 76-77 se tiene la clase TargetNotify que implementa un mecanismo de espera y notificación selectivo.
4.3. Wait y sleep
El método wait tiene una versión que permite fijar un timeout en milisegundos, al cabo del cual es hilo retoma la
ejecución aunque no se haya producido la notificación.
Esta versión de wait funciona como sleep con la diferencia de que wait no retiene el lock, por lo que se puede
utilizar dentro de un método sincronizado a diferencia de sleep.
4.4. Wait y notify en métodos estáticos
Wait y notify son métodos no estáticos por lo que no se pueden llamar dentro de métodos estáticos.
Sin embargo, se pueden utilizar los métodos wait y notify asociados a un objeto estático como en el ejemplo
MyStaticClass de la página 86 que ofrece los métodos staticWait y staticNotify que pueden utilizarse en métodos estáticos.
5. La integral numérica con hilos
5.1. Diseño
Para hacer la integral numérica con hilos de Java se han utilizado tres clases
•
La clase NumIntegralTh que es la clase principal con el método main que lanza los hilos
•
La clase IntervalTh que define cada hilo
•
La clase IntegralRes que define el acceso al resultado total en exclusión mutua
5.2. La clase NumIntegralTh
package ccpthreads;
import java.io.*;
public class NumIntegralTh {
IV-A.4
CCP
Procesamiento paralelo con hilos de Java
public static void main(String[] args) {
double a = 0.0;
double b = 0.0;
int n = 0;
int p = 0;
IntervalTh intervalTh[];
IntegralRes res = new IntegralRes();
long t0, t1;
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
System.out.println ("Enter a, b, n and p ");
try {
Double A = new Double(input.readLine());
a = A.doubleValue();
Double B = new Double(input.readLine());
b = B.doubleValue();
Integer N = new Integer(input.readLine());
n = N.intValue();
System.out.println(n);
Integer P = new Integer(input.readLine());
p = P.intValue();
} catch (IOException e) {
System.out.println (e.getMessage());
}
intervalTh = new IntervalTh[p];
for (int i=0; i<p; i++) intervalTh[i] = new IntervalTh(i, a, b, n, p, res);
t0 = System.currentTimeMillis();
for (int i=0; i<p; i++) intervalTh[i].start();
try {
for (int i=0; i<p; i++) intervalTh[i].join();
} catch (InterruptedException e) {
System.out.println (e.getMessage());
}
t1 = System.currentTimeMillis();
System.out.println ("El valor de la integral es "+res.get());
System.out.println ("El tiempo transcurrido es: "+ (t1-t0));
}
}
5.3. La clase IntervalTh
package ccpthreads;
public class IntervalTh extends Thread {
int id;
double a, b;
int n, p;
IntegralRes res;
public IntervalTh(int id, double a, double b, int n, int p, IntegralRes res) {
this.id = id;
this.a = a;
this.b = b;
this.n = n;
this.p = p;
this.res = res;
}
private double f(double x) {
double y;
y = Math.log(Math.sin(x*x+1));
y = Math.sqrt(y*y);
return y;
}
private double Trap (double a, double b, int n, double h) {
double integral;
double x;
int i;
integral = (f(a) + f(b)) / 2.0;
x = a;
for (i=1; i<n; i++) {
x = x + h;
integral = integral + f(x);
}
integral = integral*h;
IV-A.5
Procesamiento paralelo con hilos de Java
CCP
return integral;
}
public void run() {
double h;
double local_a;
double local_b;
int local_n;
double integral;
h = (b - a)/(double)n;
local_n = n / p;
local_a = a + (double)id * local_n * h;
local_b = local_a + local_n * h;
integral = Trap (local_a, local_b, local_n, h);
res.update(integral);
}
}
5.4. La clase IntegralRes
package ccpthreads;
public class IntegralRes {
private double total;
public IntegralRes() {
total = 0;
}
public synchronized void update (double v) {
total = total + v;
}
public synchronized double get() {
return total;
}
}
5.5. Versión en C
#include <stdio.h>
#include <time.h>
#include <math.h>
double f(double x) {
double y;
y = log(sin(x*x+1));
y = sqrt(y*y);
return y;
}
int main(int argc, char* argv[]) {
double integral;
float a;
float b;
int n;
double h;
double x;
int i;
clock_t t0, t1;
float t;
a = 0.0; b = 0.0; n = 0;
printf ("Enter a, b and n\n");
scanf ("%f %f %d", &a, &b, &n);
printf ("of the integral from %f to %f\n", a, b);
t0 = clock();
h = ((double)b-(double)a)/(double)n;
integral = (f(a) + f(b)) / 2.0;
x = a;
for (i=1; i<n-1; i++) {
x = x + h;
integral = integral + f(x);
IV-A.6
CCP
Procesamiento paralelo con hilos de Java
}
integral = integral * h;
t1 = clock();
t = (float)((1000*t1)/CLOCKS_PER_SEC) - (float)((1000*t0)/CLOCKS_PER_SEC);
printf ("With n = %d trapezoids, our estimate\n", n);
printf ("of the integral from %f to %f = %20.20f\n", a, b, integral);
printf ("Tiempo = %f\n", t);
return 0;
}
5.6. Tiempos de ejecución
Tabla 1. a = 0.0, b = 1.0, n = 10000000, p = 4
Sistema
Secuencial (C)
Secuencial (Java)
Paralelo
Origin 2000
7770
19588
4793
Athlon 1.7GHz linux
3620
1774
?
Bibliografía
Scott Oaks, Henry Wong "Java Threads" O'Reilly
Doug Lea "Concurrent Programming in Java: Design Principles and Patterns" Addison-Wesley
IV-A.7