Yo no soy muy de Java, pero estoy haciendo la una práctica sobre concurrencia en Java y bueno he tenido que recordar algunas cosas. Me he hecho una implementación de para la sincronización pura entre 2 hilos en Java.
Existen muchas variantes de la explicación del problema del consumidor / productor. Yo para probar la clase SincronizadorPuro he considerado el problema de la siguiente forma.
- El consumidor va a consumir N veces
- El productor va a producir N veces
- Siempre lo deben hacer alternativamente
El recurso de lo que se consume o produce debe ser protegido, y por tanto realmente estamos hablando de comunicación sincronizada, pero realmente el problema que quiero abordar es el de la sincronización pura. Además que solucionar el problema de comunicación es tan simple como hacer sincronizados los metodos get / set del atributo a proteger.
El diagrama de precedencia sería:
A—-> B —-> A —-> B —- …..—->A ——> B
Este diagrama se resuelve con 2 semaforos iniciados a 0 con máximo 1. Que es lo que abstroigo en la clase SincronizacionPura con los métodos: esperar() / avisar() que corresponden al típico wait / signal de los semáforos.
Por ello el algoritmo para A es simplemente:
- Esperará a B excepto si es la primera iteración.
- Se ejecuta el código A, en mi ejemplo pone el contador del for en el recurso compartido.
- Se avisa a B de que ha terminado.
Y analogamente, el algoritmo de B:
- Primero esperará a A siempre.
- Ejecuta el código asociado a B, que en mi caso es imprimir el valor del recurso.
- Se avisa a A excepto en la última iteración.
Código del ejemplo:
package parquetematico; import java.util.concurrent.Semaphore; class Productor implements Runnable { int INICIO; int FIN; int INC; Recurso recurso; SincronizadorPuro sync_productor; SincronizadorPuro sync_consumidor; public Productor(int INICIO, int FIN, int INC, Recurso recurso, SincronizadorPuro sync_productor, SincronizadorPuro sync_consumidor) { this.INICIO = INICIO; this.FIN = FIN; this.INC = INC; this.recurso = recurso; this.sync_productor = sync_productor; this.sync_consumidor = sync_consumidor; } public void run() { int i = INICIO; while (i <= FIN) { if(INICIO != i) sync_consumidor.esperar(); recurso.setX(i); System.out.println("produce " + recurso.getX()); sync_productor.avisar(); i+=INC; } } } class Consumidor implements Runnable { int INICIO; int FIN; int INC; Recurso recurso; SincronizadorPuro sync_productor; SincronizadorPuro sync_consumidor; public Consumidor(int INICIO, int FIN, int INC, Recurso recurso, SincronizadorPuro sync_productor, SincronizadorPuro sync_consumidor) { this.INICIO = INICIO; this.FIN = FIN; this.INC = INC; this.recurso = recurso; this.sync_productor = sync_productor; this.sync_consumidor = sync_consumidor; } public void run() { int i = INICIO; while (i <= FIN) { sync_productor.esperar(); System.out.println("consume " + recurso.getX()); if(FIN != i) sync_consumidor.avisar(); i+=INC; } } } class SincronizadorPuro { Semaphore sync; public SincronizadorPuro(int max) { // crea un semaforo en (max, max) sync = new Semaphore(max); // lo dejamos como (0, max) for(int i=0 ; i<max ; i++) { try { sync.acquire(); } catch (InterruptedException e) {} } } /* * Pseudo-implementación conceptual * WAIT / P / ESPERAR / ACQUIRE { cont--; if (cont < 0) { while (true) { try { wait(); break; } catch (InterruptedException e) { if (cont >= 0) break; else continue; } } } } */ public void esperar() { try { sync.acquire(); } catch (InterruptedException e) {} } /* * Pseudo-implementación conceptual * SIGNAL/ V / AVISAR / RELEASE { cont++; if (cont <= 0) notify(); if (cont > 1) cont = 1; } */ public void avisar() { sync.release(); } public int getContador() { return sync.availablePermits(); } } class Recurso { private int x; public Recurso(int x) { this.x = x; } public synchronized void setX(int x) { this.x = x; } public synchronized int getX() { return x; } } public class principal { public static void main(String[] args) { Recurso r1 = new Recurso(0); SincronizadorPuro sync_productor = new SincronizadorPuro(1); SincronizadorPuro sync_consumidor = new SincronizadorPuro(1); // produce/consume desde 0 hasta 5000 en pasos de 1 Productor p1 = new Productor(0,5000, 1, r1, sync_productor, sync_consumidor); Consumidor c1 = new Consumidor(0,5000, 1, r1, sync_productor, sync_consumidor); Thread h1 = new Thread(p1); Thread h2 = new Thread(c1); h1.start(); h2.start(); try { h1.join(); h2.join(); } catch(InterruptedException e) {} } }
Filed under: concurrencia, java, programación | Tagged: concurrencia, consumirdor, java, productor, semaforos, sincronizar | Leave a comment »