Wednesday, June 12, 2013

Concurrencia en Java Parte 7

Esta es la septima y última parte del tutorial, este link te lleva al post anterior concurrencia en java parte-6

Clases de utilería para sincronización

La API de concurrencia tiene varias clases para la sincronización en el paquete java.util.concurrent como Semaphore, CountDownLatch, CyclicBarrier, Phaser y Exchanger.

Semaphore

Un semáforo tiene un contador que permite el acceso a un recurso compartido, es similar a la interfaz Lock pero cuando un thread quiere adquirir un semáforo, este verifica su contador y si es mayor que cero, entonces el thread obtiene el semáforo y se reduce el contador. Sin embargo, si el contador es cero el thread espera hasta que se incremente el contador.

Ejemplo:
 public class Recurso {  
      Semaphore semaphore = new Semaphore(2);//Indica que dos threads pueden acceder el recurso al mismo tiempo.  
      public void lock() {  
           semaphore.acquire();//Si el contador es cero el thread se duerme, de lo contrario se reduce y obtiene el acceso  
           System.out.println(“Comenzando, el thread ” + Thread.currentThread().getName() + “ tiene el lock”);  
           try {                           
                Thread.sleep(10000);  
           } catch (InterruptedException e) {  
                e.printStackTrace();  
           }  
           System.out.println(“Terminando, el thread ” + Thread.currentThread().getName() + “ tiene el lock”);  
           semaphore.release();//Libera el semaphore e incrementa el countador  
      }  
 }  
 .......  

En el ejemplo, el método acquire() obtiene el semáforo si el contador es mayor que cero, de lo contrario se espera hasta que se incremente y reduce el contador, después el thread que obtiene el semáforo ejecuta el recurso y finalmente, se ejecuta el método release() y incrementa el contador.

Si el semáforo inicializa su contador con un valor de uno, entonces se llama un semáforo binario y se comporta como la interfaz java.util.concurrent.locks.Lock.

CountDownLatch

El CountdownLatch se puede utilizar para hacer esperar a un thread a que N threads hayan completado alguna acción, o algún tipo de acción se ha completado N veces. Tiene una contador y se inicializa con un valor entero, este contador es el número de acciones a esperar.

El CountdownLatch tiene el método countdown(), que reduce el contador interno y el método await() bloquea o pone a dormir un thread hasta que el contador llegue a cero.

Ejemplo:
 public class Prueba implements Runnable {    
   private CountDownLatch controlador = new CountDownLatch(10);  
   private CyclicBarrier cyclicBarrier;  
   public Prueba(CyclicBarrier cyclicBarrier) {  
      this.cyclicBarrier = cyclicBarrier;  
   }  
   public void setQuestionAnswered(){  
     controlador.countDown();  
   }  
   @Override  
   public void run() {  
      try {  
       controller.await();  
       //Empieza la evaluacion de la prueba  
      cyclicBarrier.await();//Espera a todos los threads o partidas  
     } catch (InterruptedException e) {  
       e.printStackTrace();  
     }  
   }    
   public double getTestResult() {  
   }  
 }  
 ..........  
 final Prueba [] pruebas = new Prueba[10];  
 CyclicBarrier cyclicBarrier = new CyclicBarrier(10,  
       new Runnable(){  
           public void run() {  
                //Obtiene el promedio de las pruebas  
                ..........  
           }                                
      });  
 for(int i=0; i<pruebas.length; i++)  
      pruebas[i] = new Prueba(cyclicBarrier);  
 ..........  

En el ejemplo la misma clase Prueba usada en el ejemplo anterior, pero aquí el CyclicBarrier obtendrá el promedio de todas las pruebas. Cuando todos los threads Prueba terminen el examen CyclicBarrier ejecutará el objeto Runnable que se pasa como parámetro y obtendrá el promedio de las pruebas.

Phaser

La clase Phaser es similar a CyclicBarrier y CountdownLatch pero más flexible, las tareas o subprocesos se sincronizan en pasos o fases. Las partes registradas para sincronizar en un Phaser pueden variar con el tiempo, se puede cambiar de forma dinámica con los métodos register(), bulkRegister().

El método arriveAndDeregister() notifica al Phaser que una tarea ha terminado y que no participará en las futuras fases, se reduce el número de partidos.

El método arriveAndAwaitAdvance() hace a una tarea esperar a que todos los participantes terminen.

Ejemplo:
 public class Prueba implements Runnable {      
   private Phaser phaser;  
   public Task(Phaser phaser) {  
      this. phaser = phaser;  
  }  
   @Override  
   public void run() {  
      try {  
        phaser.arriveAndAwaitAdvance();  
      //Empieza fase1       
      phaser.arriveAndAwaitAdvance();  
      //Empieza fase2       
      phaser.arriveAndAwaitAdvance();  
      //Empieza fase3                 
     } catch (InterruptedException e) {  
       e.printStackTrace();  
     }  
   }    
 }  
 ..........  
 final Prueba[] pruebas = new Prueba[10];  
 Phaser phaser = new Phaser(10);  
 for(int i=0; i<pruebas .length; i++)  
      pruebas[i] = new Prueba(phaser);       
 ..........  

En el ejemplo, el Phaser establece 10 partidas, cuando todas las tareas lleguen al primer arriveAndAwaitAdvance() se iniciará la fase 1 y luego van a tener que esperar a todos los threads antes de empezar la fase 2, y así sucesivamente.

Exchanger

La clase Exchanger sincroniza dos threads en un punto, cuando ambos threads llegan a este punto intercambiar un objeto. Exchanger puede ser visto como una forma bidireccional de un SynchronousQueue.

La clase Exchanger tiene el método exchange() el cual intercambia datos entre threads.

El ejemplo de productor consumidor puede ser implementado con este mecanismo.

Ejemplo:
 public class Consumidor extends Thread {  
      private Exchanger <String> exchanger;  
      public Consumidor(Exchanger exchanger) {  
           this.exchanger = exchanger;  
      }  
      public void run() {  
           String mensaje = "";  
           while (!mensaje.equalsIgnoreCase("fin")) {  
                try {  
                     mensaje = exchanger.exchange(mensaje);//Espera e intercambia el mensaje con el productor  
                     System.out.print( mensaje + " ");  
                } catch (InterruptedException e) {  
                     e.printStackTrace();  
                }  
           }            
      }       
 }  
 public class Productor extends Thread {  
      private Exchanger exchanger;  
      public Productor(Exchanger exchanger) {  
           this.exchanger = exchanger;  
      }  
      public void run() {  
           String [] mensajes = {"Hola", "mundo", "fin"};  
           for (String mensaje:mensajes) {  
                try {  
                     exchanger.exchange(mensaje);//Espera e intercambia el mensaje con el consumidor;                 
                } catch (InterruptedException e) {  
                     e.printStackTrace();  
                }  
           }  
      }            
 }  
 .......  
      Exchanger <String> exchanger = new Exchanger<String>();   
      Consumidor consumidor = new Consumidor(exchanger);  
      Productor productor = new Productor(exchanger);  
      consumidor.start();  
      productor.start();  
 .......  


Este ejemplo es similar al de la parte 4, pero en lugar de tener una clase que actúa como un monitor, aquí el Exchanger hace ese trabajo y sincroniza los mensajes entre los threads.

No comments:

Post a Comment