Curso Java. Volumen III. Regiones críticas. Ejemplo avanzado

Escrito por Sergio De Luz
Java
2

En el volumen anterior vimos cómo el uso del método synchronized solucionaba el uso de variables compartidas, y que por fin podíamos tener un programa correcto usando múltiples hilos.

Vimos cómo el resultado era bastante diferente, pero al ser un ejemplo de iniciación, tampoco se veía mucho la diferencia.

A continuación os mostramos un código en el que las diferencias son muy significativas…y que por ejemplo, si estamos haciendo un programa para lanzar un cohete, puede que llegue a España en lugar de a la Estación Espacial Internacional…o que ni siquiera se mueva de tierra.

Clase Calculador.java

public class Calculador extends Thread {

int desde, hasta;
Resultado r;

public Calculador(int x, int y, Resultado r) {
this.desde = x;
this.hasta = y;
this.r = r;
}

@Override
public void run() {
for (int i = desde; i <= hasta; i++) {
if (esPrimo(i)) {
r.sumar(i);
}
}
}

private boolean esPrimo(int n) {
int raiz = (int) Math.sqrt((double) n);
for (int i = 2; i <= raiz; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
}

Clase Resultado.java

public class Resultado {
private BigInteger suma = new BigInteger("0");
public BigInteger getSuma() {
return suma;
}
public void sumar(int n) {
BigInteger bn = new BigInteger(String.valueOf(n));
suma = suma.add(bn);
}
}

Ahora os voy a proponer dos códigos, uno que se hace de forma secuencial y otro de forma concurrente con varios hilos.

Programa de forma secuencial:

Como podéis ver, hay sólo un objeto p1, por lo que es como si fuera secuencial.

public class SumaPrimosSecuencial {

public static void main(String[] x) {
Resultado suma = new Resultado();
long t0 = (new Date()).getTime();
Calculador p1 = new Calculador(1, 10000000, suma);
try {
p1.join();
} catch (InterruptedException e) {
}
long t1 = (new Date()).getTime();
System.out.println("La suma de los números primos hasta 10000000 es: " + suma.getSuma() + " calculado en " + (t1 - t0) + " miliseg.");
}
}

Y ahora de forma concurrente:

public class SumaPrimosConcurrente {

public static void main(String[] x) {
Resultado suma = new Resultado();
long t0 = (new Date()).getTime();
Calculador p1 = new Calculador(1, 2000000, suma);
Calculador p2 = new Calculador(2000001, 4000000, suma);
Calculador p3 = new Calculador(4000001, 6000000, suma);
Calculador p4 = new Calculador(6000001, 8000000, suma);
Calculador p5 = new Calculador(8000001, 10000000, suma);

try {
p1.join();
p2.join();
p3.join();
p4.join();
p5.join();
} catch (InterruptedException e) {
}
long t1 = (new Date()).getTime();
System.out.println("La suma de los números primos hasta 10000000 es: " + suma.getSuma() + " calculado en " + (t1 - t0) + " miliseg.");
}
}
  • Fijaos en la diferencia de tiempo entre una tarea y otra, realmente se han calculado el mismo número de primos por lo que la ganancia en tiempo es sustancial.
  • Fijaos también en la suma de números primos que es igual en ambos… oh wait! no es igual, ¿verdad? ¿Qué ha pasado? Pues que no hemos controlado la región crítica y el programa no es correcto.

¿Qué tenemos que hacer para solucionarlo? Muy fácil, en la clase Resultado.java poner:

public synchronized void sumar(int n)

Y os dará el mismo valor, ¿ veis la importancia de controlar esto? En el próximo artículo hablaremos sobre teoría de Threads así como ejemplos.


Continúa leyendo
  • edson

    bueno copie tu codigo y no sale la suma se los numeros primos lo que deberia hacer

    • Oscar Sánchez Guevara

      En la ejecucion del código secuencial debias agregar un start(); (el del unico hilo que se usa) antes del try{} ; es decir:

      etc..
      p1.start();
      try{
      p1.join();
      } catch
      etc….

      Y en la ejcución del códido con hilos, debias agregar un start por cada hilo; es decir:

      etc…
      p1.start(); p2.start(); p3.start(); p4.start(); p5.start();
      try {
      p1.join();
      p2.join();
      p3.join();
      p4.join();
      p5.join();
      } catch
      etc-….

      OJO: también se pueden poner dentro del try (pero antes de los join();)