Monday, May 27, 2013

Concurrencia en Java Parte 5

Esta es la quinta parte del tutorial, este link te lleva al  post anterior concurrencia en java parte-4

Executor Framework

Cuando un programa ejecuta muchas tareas concurrentes, todo el código relacionado con los threads tiene que ser implementado, se tiene que crear un objeto por thread, ejecutar el thread, obtener sus resultados, y así sucesivamente. Esto puede traer algunos problemas tales como no administrar de manera eficiente los recursos de la máquina y afectar el rendimiento de la aplicación. Para aplicaciones de gran tamaño se necesita un mejor enfoque y el executor freamwork puede ayudar con esto.

El executor freamwork separa la tarea de creación del thread, la ejecución y su administración, encapsula la funcionalidad y mejora el rendimiento usando un “pool” de threads

La forma en que el executor freamwor trabaja es muy simple, sólo requiere de instancias de objetos de tipo Runnable o Callable y el se encarga del resto.

Ejemplo:
 public class Servidor {  
             private ThreadPoolExecutor executor;  
             public Servidor() {  
               executor = (ThreadPoolExecutor)Executors.newCachedThreadPool();//Crea el objeto executor o un pool de threads  
             }  
             public void ejecutaTarea(Task tarea) {  
                    executor.execute(tarea);// Ejecuta una tarea 
             }  
           public void terminaServidor () {  
                    executor.shutdown();// Este metodo termina el executor  
             }  
 }  
 public class Tarea implements Runnable {  
   private String nombre;  
   public Task(String nombre) {      
     this.nombre = nombre;  
   }  
   public void run() {  
     System.out.println(Thread.currentThread().getName() + ", creado: " + new Date());  
     try {  
       TimeUnit.SECONDS.sleep((long) (Math.random() * 10));  
     } catch (InterruptedException e) {  
       e.printStackTrace();  
     }  
     System.out.println(Thread.currentThread().getName() + ", terminado: " + new Date());       
   }  
 }  
 ...........  
     Servidor servidor = new Servidor();  
     for (int i = 0; i < 10; i++) {  
       Tarea tarea = new Tarea("Tarea " + i);  
       servidor.ejecutaTarea(tarea);  
     }  
     servidor.endServer();  
 ...........  

En el ejemplo anterior, la clase Executors creo un objeto de tipo java.util.concurrent.ThreadPoolExecutor, esta clase es una implementación de la interfaz java.util.concurrent.ExecutorService. Aunque ThreadPoolExecutor puede ser creado usando directamente sus constructores, se recomienda utilizar la clase Executors.

El ThreadPoolExecutor utiliza el método execute() para ejecutar un Runnable o Callable. Tambien tiene otros métodos como getPoolSize(), getCompleteTaskCount () para obtener el estado del pool.
El ThreadPoolExecutor tiene que ser terminada explícitamente llamando al método endServer(), de lo contrario no termina y el programa nunca terminará.

Para evitar saturar la aplicación y provocar un mal rendimiento, la clase Executors tiene el método newFixedThreadPool(int nthreads) que crea un executor de threads de tamaño fijo. Este executor tiene un número máximo de threads indicados por el parametrio nthreads, y como dice el java api "En cualquier momento, como máximo un numero “nthreads ” de threads estarán activos ejecutando tareas. Si tareas adicionales son enviadas cuando todos los threads estén activos, van a tener que esperar en la cola hasta que un thread este disponible. Si cualquier thread termina debido a un error durante la ejecución antes de terminar el executor, uno nuevo tomará su lugar si es necesario para ejecutar tareas posteriores ".

Ejemplo:
   ......  
   public Servidor() {      
     executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);  
   }  
   ......  

En el ejemplo se crea un executor con un máximo de 5 threads, esto significa que si más de 5 tareas se envían a ejecutar sólo 5 se ejecutaran y el resto se bloqueará hasta que haya un thread libre para procesarlos.

Tareas que regresan un valor

El executor framework puede ejecutar tareas que devuelven un valor, esta es otra ventaja de utilizar este framework. Para este mecanismo se utiliza la interfaz java.util.concurrent.Callable, en lugar de tener un método run(), se hace una llamada al call() que devuelve cualquier tipo de objeto que es especificado de forma genérica:

                  public interface Callable<V> {
                       V call() throws Exception;
                  }

El ExecutorService tiene el método submit() que acepta objetos de tipo Callable y los ejecuta, este método devuelve un objeto del tipo java.util.concurrent.Future, la interfaz Future tiene métodos para obtener el resultado generado por el objeto Callable.

Ejemplo:
 public class CalculadorMultiplicacion implements Callable<Integer> {  
   private int operador1;  
   private int operador2;  
   public CalculadorMultiplicacion(int operador1, int operador2) {  
     this.operador1 = operador1;  
     this.operador2 = operador2;  
   }  
   public Integer call() throws Exception {  
     return operador1 * operador2;  
   }  
 }  
 .......................  
 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(2);//Maximo 2 threads   
  List<Future<Integer>> listaResultados = new ArrayList<Future<Integer>>();  
  for (int i = 0; i < 10; i++) {  
       CalculadorMultiplicacion calculador = new CalculadorMultiplicacion((int)(Math.random()*10), (int)(Math.random()*10));  
       Future<Integer> resultado = executor.submit(calculador);  
       listaResultados.add(resultado);  
     }  
     while (executor.getCompletedTaskCount() < listaResultados.size()) {  
         try {  
         Thread.sleep(50);  
       } catch (InterruptedException e) {  
         e.printStackTrace();  
       }  
     }//Espera a que las tareas se completen  
     for (int i = 0; i < listaResultados.size(); i++) {  
       Future<Integer> resultado = listaResultados.get(i);  
       try {        
         System.out.println("El resultado de la tarea "+i+ " es:" + resultado.get());  
       } catch (Exception e) {  
         e.printStackTrace();  
       }   
     }  
 .......................  

En el ejemplo anterior, los objetos Callable realizan una operación de multiplicación se envían al executor utilizando el método de submit(), el programa espera hasta que todas las tareas hayan acabado verificando con el método del executor getCompletedTaskCount (), una vez que terminen los resultados se obtienen con el método get() del objeto Future.

El ExecutorService tiene el método invokeAny(tareas) que recibe una colección de tareas luego las ejecuta y devuelve el resultado de la primer tarea que termina sin lanzar una excepción, las tareas que no han finalizado se cancelan.

Un ejemplo en el que se podría utilizar este método podría ser para buscar servicios, una aplicación que quiere encontrar una conexión de base de datos en servidores diferentes, la primera tarea que encuentre disponible el servicio es el que se va a utilizar, las demás tareas son ignoradas.

El ejemplo anterior implementado con el método invokeAny () quedaría de la siguiente forma:
 .................  
 ExecutorService executor = (ExecutorService) Executors.newCachedThreadPool();  
     List<CalculadorMultiplicacion> listaTareas= new ArrayList<CalculadorMultiplicacion>();  
     for (int i = 0; i < 10; i++) {  
       CalculadorMultiplicacion calculador = new CalculadorMultiplicacion((int)(Math.random()*10), (int)(Math.random()*10));  
       listaTareas.add(calculador);  
     }  
     try {  
        Integer resultado = executor.invokeAny(listaTareas);  
        System.out.println("El resultado de la primer tarea en terminar es:" + resultado);  
     } catch (Exception e) {e.printStackTrace();}  
     //Termina el Executor  
     executor.shutdown();  
  .............  

El ExecutorService tiene otro mecanismo para ejecutar múltiples tareas y procesar el resultado de todas las tareas, el método invokeAll (tareas) recibe una colección de tareas, los ejecuta y devuelve una lista de objetos de tipo Future.

Ejemplo:
 ExecutorService executor = (ExecutorService) Executors.newCachedThreadPool();
     List<CalculadorMultiplicacion> listaTareas = new ArrayList<CalculadorMultiplicacion>();
     for (int i = 0; i < 10; i++) {
       CalculadorMultiplicacion calculador = new CalculadorMultiplicacion((int)(Math.random()*10), (int)(Math.random()*10));
       listaTareas.add(calculador);
     }    
     List<Future<Integer>> listaResultados = null;      
     try {
        listaResultados = executor.invokeAll(listaTareas);      
     } catch (Exception e) {e.printStackTrace();}    
     executor.shutdown();
     for (int i = 0; i < listaResultados.size(); i++) {
       Future<Integer> resultado = listaResultados.get(i);
       try {      
         System.out.println("El resultado de la tarea "+i+ " es:" + resultado.get());
       } catch (Exception e) {
         e.printStackTrace();
       }
     }

En el ejemplo en lugar de enviar cada tarea para el executor con el método submit(), todas las tareas se agrupan en una lista y se enviar para ejecutar a través de el método invokeAll().

Tareas programadas

La clase Executores puede crear un “pool” que programa las tareas después de un cierto tiempo, o las ejecuta periódicamente. Este “pool” implementa la interfaz java.util.concurrent.ScheduledExecutorService.

Ejemplo:
 ScheduledExecutorService executor=(ScheduledExecutorService)Executors.newScheduledThreadPool(1);  
 List<Future<Integer>> listaResultados = new ArrayList<Future<Integer>>();  
 for (int i=0; i<5; i++) {  
     CalculadorMultiplicacion calculador = new CalculadorMultiplicacion((int)(Math.random()*10), (int)(Math.random()*10));  
     Future resultado<Integer> = executor.schedule(calculacor,i+1 , TimeUnit.SECONDS);  
     listaResultados.add(resultado);  
 }  
 executor.shutdown();  
 //Espera la terminación del executor  
 try {  
      executor.awaitTermination(1, TimeUnit.DAYS);  
 } catch (InterruptedException e) {  
      e.printStackTrace();  
 }  
 ..............  

En el ejemplo se crea un “pool” programado con un tamaño de 1, cada thread es programado utilizando el método schedule(), este método recibe como parámetros la tarea a ejecutar, el período de tiempo de espera antes de la ejecución y la unidad de tiempo .
El executor utiliza el método awaitTermination() que bloquea hasta que todas las tareas se han completado o pase el tiempo de espera .

Tareas rechazadas

Si una tarea se enviá al executor entre la llamada al método shutdown() y el final de su ejecución, la tarea se rechaza. El ejecutor proporciona un mecanismo para manejar esto, sólo requiere una instancia de un objeto que implementa la interfaz java.util.concurrent.RejectedExecutionHandler.

Example:
 public class RejectedTaskController implements RejectedExecutionHandler {  
   public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {  
     System.out.println("La tarea ha sido rechazada");     
   }  
 }  
 ........  
 RejectedTaskController controller = new RejectedTaskController();      
 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newCachedThreadPool();  
 executor.setRejectedExecutionHandler(controller);  
 ........  


Cuando se rechaza una tarea el método rejecedExecution() de la instancia de RejectedExecutionHandler es llamado.

Ir a parte 6

Monday, May 20, 2013

Concurrencia en Java Parte 4

Esta es la cuarta parte del tutorial, este link te lleva al  post anterior concurrencia en java parte-3


Thread Deadlock

Deadlock sucede cuando dos threads se bloquean en espera del lock de un objeto que cada thread posee. Ni uno de los threads se puede ejecutarse hasta que el otro abandone su lock, por lo que esperarán para siempre o se bloqueara indefinidamente.

Ejemplo:

 public class Deadlock extends Thread {  
           Recurso a;  
           Recurso b;  
           public Deadlock(Recurso a, Recurso b,String nombre) {  
                super(nombre);  
                this.a = a;  
                this.b = b;  
           }  
           public void run(){  
                synchronized(a) {  
                       try {                           
                          Thread.sleep(10000);  
                     } catch (InterruptedException e) {  
                          e.printStackTrace();  
                     }  
                     synchronized(b) {  
                     }  
                     System.out.println("Thread " + this.getName() + " ha terminado");       
                }  
           }  
      }  
      ..........  
      Recurso a = new Recurso();  
      Recurso b = new Recurso();  
      Deadlock d1 = new Deadlock(a, b, "uno");  
      Deadlock d2 = new Deadlock(b, a, "dos");  
      d1.start();  
      d2.start();  
      ..........  

En el ejemplo anterior ningún mensaje se imprime en consola porque los threads nunca llegan a la sentencia System.out.println(), ambos threads quedan para siempre esperando en la segunda sentencia synchronized().

Lo que sucede es que el primer thread "d1" adquiere el lock del recurso "a" y el segundo thread "d2" adquiere el lock del recurso "b", entonces los dos threads duermen durante 10 segundos, el tiempo de que duermen es para dar tiempo a cada thread de ejecutarse y adquirir el lock, luego después de despertarse el deadlock sucede, cada thread tratará de obtener el recurso que el otros posee, "d1" tratara de adquirir "b" y "d2" tratará de adquirir "a".

Una forma de evitar los deadlocks es utilizar la interfaz Lock y obtener el lock de los objetos con el método tryLock() .

Interacción de Threads

Temas pueden comunicar a otros threads el estado de un evento. La API de Java proporciona dos formas de habilitar esta comunicación.
  1. Los métodos de ayuda de la clase Object (wait(), notify(), notifyAll()).
  2. La interfaz java.util.concurrent.locks.Condition.
Estos mecanismos permiten a un thread ponerse en un estado de espera hasta que otro thread lo despierte si hay una razón para volver del estado de espera.

Un thread tiene que llamar a los metodos wait(), notify(), notifyAll() de un objeto desde un contexto sincronizado o de lo contrario una IllegalMonitorStateException se lanzara. El thread debe sincronizar la instancia del objeto en el que está llamando a los métodos de ayuda.

Ejemplo:

 public class Calculador extends Thread {  
       private Operacion op;  
       public void run() {  
          synchronize(op) {  
             op.wait();  
          }  
       }  
 }  


Existe una versión sobrecargada del método wait() que espera una cantidad de tiempo, por lo que el thread espera hasta que sea notificado o transcurra el tiempo especificado.

Cuando se invoca el método wait() en un objeto, el thread que ejecucta ese código libera el lock del objeto, sin embargo, cuando notify() o notifyAll() son invocados en un objeto el thread no renuncia al lock, el lock se libera hasta que el código sincronizado donde el notify() o notifyAll() se utilizan es completado.

Un ejemplo común de interacción de threads es el consumidor-productor, en este tipo de programa de dos threads interactúan enviando mensajes entre ellos a través de un "monitor". El thread productor como su nombre lo dice genera mensajes para el consumidor, por otro lado el thread consumidor espera a los mensajes generado por el productor.

Ejemplo:

 public class Correo {  
      private String mensaje;  
      private boolean vacio = true;  
      public synchronized String recibir() {  
           //Espera hasta que el mensaje este disponible  
           while(empty){  
                try {  
                     wait();  
                } catch (InterruptedException e) {  
                     e.printStackTrace();  
                }                 
           }  
           vacio=true;  
           notifyAll();  
           return mensaje;  
      }  
      public synchronized void enviar(String mensaje){  
           //Espera hasta que el mensaje sea leido  
           while(!vacio) {  
                try {  
                     wait();  
                } catch (InterruptedException e) {  
                     e.printStackTrace();  
                }  
           }  
           vacio = false;  
           this.mensaje = mensaje;  
           notifyAll();  
      }  
 }  
 public class Productor extends Thread {  
      private Correo correo;  
      public Producer(Correo correo) {  
           this.correo = correo;  
      }  
      public void run() {  
           String [] mensajes = {"Hola", "mundo", "fin"};  
           for (String men:mensajes) {  
                correo.enviar(men);  
           }  
      }       
 }  
 public class Consumidor extends Thread {  
      private Correo correo;  
      public Consumer( Correo correo) {  
           this. correo = correo;  
      }  
      public void run() {  
           String mensaje = "";  
           while (!mensaje.equalsIgnoreCase("fin")) {  
                mensaje = correo.recibir();  
                System.out.print(mensaje + " ");  
           }  
      }  
 }  
 ..............  
 Correo correo = new Correo();   
 Consumidor consumidor = new Consumidor(correo);  
 Productor productor = new Productor(correo);  
 consumidor.start();  
 productor.start();  
 ..............  

En el ejemplo de la clase de Correo actúa como un monitor, si no sabes lo que un monitor ver este link http://en.wikipedia.org/wiki/Monitor_(synchronization), esta clase hace que el consumidor deba esperar si hay ningún mensaje para leer y le notifica cuando llega un mensaje. Es lo mismo para el productor, lo hace esperar si el productor quiere enviar un mensaje y el último mensaje que se mando no se ha leído y le notifica cuando el mensaje se ha leído.
Por lo tanto, la clase Correo a través de sus métodos wait(), notify(), notifyAll() hace más fácil la interacción entre los threads consumidores y productores, es importante tener en cuenta que estos métodos se utilizan dentro de ciclos de condición, de esta manera es más seguro de usar este métodos y evitar situaciones de tipo deadlock.

La interfaz java.util.concurrent.locks.Condition proporciona una funcionalidad similar a los métodos de ayuda de la clase Object.
Las condiciones se asocian con locks, las condiciones permiten a los threads tener el control de un lock y comprobar si una condición es verdadera o no, si es falsa, se suspende el thread hasta que otro thread los despierte.

El API de Java dice lo siguiente acerca de la interfaz Condition:
La interfaz Condition provee métodos como los métodos monitor de la clase Object (wait, notify y notifyAll). Cuando un Lock reemplaza el uso de los métodos y declaraciones sincronizados, una condición reemplaza el uso de los métodos objeto de monitor.

Todas las condiciones están asociadas a un lock y para obtener una instancia de una condición para un lock específica, se usa el método newCondition().

                               Lock lock = new ReentrantLock();
                               Condition condicion = lock.newCondition();

La interfaz Condition tiene los métodos await(), signal(), signalAll() y su comportamiento es igual que los métodos wait(), notify(), notifyAll() de la clase Object. Estos métodos deben estar en un bloque de código que comienza con una llamada al método lock() de un objeto de tipo Lock y termina con un método de unlock() en el mismo objeto, o una excepción IllegalMonitorStateException se lanzara.

 
                                          
            lock.lock();//Obtiene el lock  
               try {  
                   condicion.await();             
            } catch (InterruptedException e) {  
                 e.printStackTrace();  
            } finally {  
                 lock.unlock();//Libera el lock  
               }  

Para implementar el ejemplo de consumidor-productor con condiciones, sólo tenemos que cambiar la clase de Correo la que actúa como monitor, hay que retirar las palabras clave synchronized por un lock con una condición, y cambiar los métodos de ayuda de la clase Object por los de la condición.

 public class Correo {  
      private String mensaje;  
      private boolean vacio = true;  
      private Lock lock = new ReentrantLock();  
      private Condition condicion = lock.newCondition();  
      public String recibir() {  
           //Espera hasta que el mensaje este disponible  
           lock.lock();  
           while(vacio){                 
                try {  
                     condicion.await();  
                } catch (InterruptedException e) {  
                     e.printStackTrace();  
                }                           
           }  
           vacio=true;  
           condicion.signalAll();  
           lock.unlock();  
           return mensaje;  
      }  

      public void enviar(String mensaje){  
           //Espera hasta que el mensaje sea leido  
           lock.lock();  
           while(!vacio) {  
                try {  
                     condicion.await();  
                } catch (InterruptedException e) {  
                     e.printStackTrace();  
                }                 
           }  
           vacio = false;  
           this. mensaje = mensaje;  
           condicion.signalAll();  
           lock.unlock();  
      }  
 }  


En el ejemplo anterior, el método de la await() de la condición actúa como el método wait() de la clase Object y signalAll() actúa como notify(), y el comportamiento debe ser el mismo.

Ir a parte 5

Monday, May 13, 2013

Concurrencia en Java Parte 3

Esta es la tercera parte del tutorial, este link te lleva al  post anterior concurrencia en java parte-2


SINCRONIZACIÓN DE THREADS

Una situación común en programas con múltiples threads es cuando dos threads comparten o tienen acceso a un solo recurso, como un archivo, la conexión de base de datos o un objeto. Esta situación puede provocar situaciones de error o inconsistencia de datos.

Para garantizar que sólo un thread ejecutará una sección crítica de código, evitar la inconsistencia de datos y errores, los mecanismos de sincronización deben de implementarse.

Java provee dos mecanismos de sincronización:
  • La palabra clave synchronized.
  • La interfaz java.util.concurrent.locks.Lock y sus implementaciones.

Cada objeto en java tiene de defecto un lock, que sólo entra en juego cuando el objeto tenga sincronizado un bloque de código.

Si un thread tiene el lock de un objeto, ningún otro thread puede tener el lock hasta que el primer thread lo libere. Un thread puede adquirir más de una lock.

 Ejemplo:

 public class Resource {  
           public synchronized void lock() {  
           System.out.println(“Begining, the thread ” + Thread.currentThread().getName() + “has the lock”);  
                try {                           
                     Thread.sleep(10000);  
                } catch (InterruptedException e) {  
                     e.printStackTrace();  
                }  
           System.out.println(“Ending, the thread ” + Thread.currentThread().getName() + “has the lock”);  
           }  
      }  
      public class Concurrent extends Thread {  
           Resource resource;  
           public Concurrent( Resource resource, String name) {  
                super(name);  
                this. resource = resource;  
           }  
           public void run(){  
                resource.lock();  
           }  
      }  
      .....  
      Resource resource = new Resource();  
      Concurrent c1 = new Concurrent(resource, “one”);  
       c1.start();  
      Concurrent c2 = new Concurrent(resource, “two”);  
      c2.start();  
      ......  


En el ejemplo anterior dos threads acceden concurrentemente un recurso, pero un thread tiene que esperar al otro a que termine de utilizar al recurso ya que el recurso está protegido por la palabra clave synchronized, no hay garantía de que thread adquirirá el recurso primero.

Los mensajes en la consola deben de aparecer de la siguiente manera:

Comenzando, el thread uno tiene el lock
Finalizando, el thread uno tiene el lock
Comenzando, el thread dos tiene el lock
Finalizando, el thread dos tiene el lock

 o

Comenzando, el thread dos tiene el lock
Finalizando, el thread dos tiene el lock
Comenzando, el thread uno tiene el lock
Finalizando, el thread uno tiene el lock

Los mensajes para cada thread aparecen consecutivamente y no se mezclan, ya que cada thread no libera el lock del objeto Recurso hasta que termina de ejecutar el método lock ().

Si la palabra clave synchronized se retira de la declaración de la lock () en la clase Recurso, los resultados del programa podría ser cualquiera, un ejemplo de los mensajes en la consola sería:

Comenzando, el thread dos tiene el lock
Comenzando, el thread uno tiene el lock
Finalizando, el thread uno tiene el lock
Finalizando, el thread dos tiene el lock

La palabra clave synchronized se puede aplicar a una instancia de un objeto para proteger el acceso de un bloque de código en lugar de un método, tiene el mismo comportamiento, el thread que accesa el bloque de de código intenta adquirir el lock del objeto.

Ejemplo:

 public class Recurso {  
           public void lock() {  
                 synchronized(this) {  
           System.out.println(“Comenzando, el thread ” + Thread.currentThread().getName() + “tiene el lock”);  
                     try {                           
                          Thread.sleep(10000);  
                     } catch (InterruptedException e) {  
                          e.printStackTrace();  
                     }  
           System.out.println(“Finalizando, el thread ” + Thread.currentThread().getName() + “tien el lock”);       
                }  
           }  
      }  
      ........  


El otro mecanismo de sincronización proporcionada por Java es la interfaz Lock y sus implementaciones, de acuerdo con la API de Java “las implementaciones de Lock proporcionan más operaciones que las que pueden obtenerse utilizando métodos y sentencias sincronizadas. Permiten una estructuración más flexible, puede tener propiedades muy diferentes, y puede soportar múltiples objetos de tipo Condition asociados ".

La interfaz Lock proporciona varios métodos para obtener un Lock de un objeto con diferentes comportamientos.

El siguiente ejemplo es el mismo que el utilizado anteriormente con la palabra clave synchronized.
Ejemplo:

 public class Recurso {  
           Lock objetoLock = new ReentrantLock();  
           public void lock() {  
                objetoLock.lock();//Adquiere el lock  
           System.out.println(“Comenzando, el thread ” + Thread.currentThread().getName() + “tiene el lock”);  
                try {                           
                     Thread.sleep(10000);  
                } catch (InterruptedException e) {  
                     e.printStackTrace();  
                }  
           System.out.println(“Finalizando, el thread ” + Thread.currentThread().getName() + “tiene    el lock”);  
                objetoLock.unlock();//Libera el lock  
           }  
      }  
      .......  



En el ejemplo se utilizó la implementación ReentrantLock de la interfaz Lock, pero la API proporciona más implementaciones con diferente comportamiento de cada una.

ReentrantLock tiene otro constructor que acepta un parámetro booleano conocido como el parámetro fair, y si es falso indica que cualquier thread que espera por el Lock lo recibirá, pero si es verdadero
indica que el thread que ha estado esperando el mayor tiempo por el Lock lo recibirá.

                           Lock lock = new ReentrantLock(true);//fair es verdadero

El método lock() intenta adquirir el lock del objeto Lock, si otro thread tiene el lock del objeto, el thread esperara hasta que el thread que tiene el lock lo libere.



Cuando un thread termina de usar un lock debe llamar al método unlock() para liberar el lock para que otros thread pueden acceder al lock.

La interfaz Lock tiene le método tryLock(), y este es similar al método lock(), pero este método no se bloquea esperando un lock como el método lock() lo hace, este devuelve un valor booleano que indica si el lock fue adquirido o no, devuelve verdedero si la lock estaba libre y fue adquirido por el falso actual, y false en caso contrario.
Este método debe ser usado en una condición if(), si se obtiene el lock entonces un bloque de código se ejecutara.

                          If (objetoLock.tryLock()) {
                                .........
                          }

Ir a parte 4

Monday, May 6, 2013

Concurrencia en Java Parte 2

Esta es la segunda parte de mi post original Concurrencia en java parte-1


Prioridades en Threads

Todos los threads tienen un valor de prioridad entre 1 y 10. En la mayoría de los casos, el thread en ejecución tendrá la misma prioridad que la máxima prioridad de los threads del pool.
Si algunos threads tienen la misma prioridad, es el scheduler (programador) el que decide que thread ejecuta.

Por defecto, un thread obtiene su prioridad de el thread de ejecución que lo creó, pero se puede cambiar a través del método setPriority(int), este método puede lanzar una IllegalArgumentException si el valor se establece que no está entre 1 y 10.

El método estático yield() de la clase Thread indica a la JVM que el thread en ejecución puede dejar el CPU e ir a estado runnable, permitiendo a otros threads con la misma o mayor prioridad ejecutarse. La JVM no garantiza este comportamiento.

Thread durmiendo


El método estático sleep(long) de la clase Thread hace que el que el thread en ejecución se detenga por lo menos la duración especificada en milisegundos indicada por el parámetro.
Al terminar el tiempo de dormir y la JVM asigna tiempo de CPU al thread dormido, y continúa en la instrucción siguiente a la llamada del método sleep ().

Ejemplo:

           public class Calculador extends Thread {
          public void run() {
      System.out.println(“Thread en ejecucion:” + this.getName() + “, comenzado en:” + Calendar.getInstance().getTimeInMillis());
                    try {
                        Thread.sleep(5000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
      System.out.println(“Thread en ejecucion:” + this.getName() + “, terminado :” + Calendar.getInstance().getTimeInMillis());
         }         
    }
Los mensajes la consola deben tener una diferencia de tiempo de por lo menos de 5 segundos.

Excepciones

La excepción InterruptedException es lanzada por algunos métodos relacionadas con la API de concurrencia de java, para indicar que el thread ha sido interrumpido.

Si no sabes que es una excepción de tipo “checked” o “unchecked” , ve a este link:

Las excepciones de tipo “checked” lanzadas dentro del método run() de un objeto thread se deben atrapar y tratarse, porque el método run() de la interfaz Runnable no declara ninguna cláusula throws.

Las excepciones de tipo “unchecked” lanzadas dentro del método run() de un objeto thread no requieren atraparse y la mayoría de las veces hacen que el programa termine. Para las excepciones de tipo “unchecked” Java proporciona una forma de detectar y tratar las excepciones en un objeto thread para evitar que el programa finalice.

Los pasos para implementar esto son:
  1. Cree una clase que implemente Thread.UncaughtExceptionHandler.
  2. Establecer el manejador de excepciones al objeto thread.



Ejemplo:

          public class ManejadorException implements UncaughtExceptionHandler {

               public void uncaughtException(Thread t, Throwable e) {
                    System.out.println("Una excepcion ha sido atrapada");
                    e.printStackTrace(System.out);
                    System.out.println("Thread status: " + t.getState());
               }
          }



          public class Calculador implements Thread {
               public void run() {
                    throw new RuntimeException(“Excepcion lanzada”);
               }
          }

          ............
          Calculador calculador = new Calculador();
          calculador.setUncaughtExceptionHandler(new ManejadorException());
          calculador.start();

          ............

La consola debe imprimir los mensajes del manejador de excepciones.

La clase Thread tiene otro método para controlar las excepciones de tipo “unchecked”, es el metodo estatico setDefaultUncaughtExceptionHandler(), este método establece un manejador de excepciones para todos los threads en una aplicación.


Fabrica de threads.

El patrón de diseño de fábrica es un patrón creacional cuya misión es crear objetos. Este patrón provee algunas ventajas como la facilidad de cambiar la forma en que se crean objetos, limitar la creación de los recursos, y así sucesivamente.

La interfaz java.util.concurrent.ThreadFactory proporciona una forma de implementar una fábrica objetos de tipo Thread. Sólo tiene el método newThread() que recibe un objeto Runnable como  parámetro y devuelve un objeto Thread.

Ejemplo:

         public class Fabrica implements ThreadFactory {
              private int contador = 1;

              public Thread newThread(Runnable r) {
                   Thread t=new Thread(r, "Thread: " +contador);
                   contador++;
                   return t;
              }
         }

         public class Calculador implements Runnable {
               public void run() {
                    .......
               }
         }

         ....
         Fabrica fabrica=new Fabrica();
         Calculador calculador = new Calculador ();
         for (int i=0; i<10; i++){
                Thread thread=fabrica.newThread(calculador);
                thread.start();
          }
          .....

Después del ciclo for 10 objetos thread han sido creados e inicializados.


Ir a parte 3