Archivo de la categoría: DEBUN_OCJP7

OCP7 11 – Hilos (09) – E/S Simultánea

Las llamadas de bloqueo secuencial se ejecutan en una duración de tiempo más larga que las llamadas de bloqueo simultáneo.

E/S Simultánea
E/S Simultánea

Reloj: Existen diferentes formas de medir el tiempo.

En el gráfico se muestran cinco llamadas secuenciales a servidores de red que tardarán aproximadamente 10 segundos si cada llamada dura 2 segundos.

En la parte derecha del gráfico, cinco llamadas simultáneas a los servidores de red solo tardan un poco más de 2 segundos si cada llamada dura 2 segundos.

Ambos ejemplos usan aproximadamente la misma cantidad de tiempo de CPU, la cantidad de ciclos de CPU.

Seguir leyendo OCP7 11 – Hilos (09) – E/S Simultánea

OCP7 11 – Hilos (07) – Fork Join

Necesidad de un marco Fork-Join

La división de conjuntos de datos en subconjuntos con el mismo tamaño para cada thread de ejecución tiene un par de problemas.

Marco Fork-Join
Necesidad de un marco Fork-Join

Lo ideal es que todas las CPU se utilicen completamente hasta que la tarea finalice pero:

  • Las CPU se pueden ejecutar a diferentes velocidades.
  • Las tareas que no son de Java requieren tiempo de CPU y pueden reducir el tiempo del que dispone un thread de Java para la ejecución en una CPU.
  • Los datos que se analizan pueden requerir diferentes cantidades de tiempo para el proceso.

Seguir leyendo OCP7 11 – Hilos (07) – Fork Join

OCP7 11 – Hilos (08) – Paralelismo

Introducción

Los sistemas modernos contienen varias CPU. Para sacar partido de la potencia de procesamiento en un sistema es preciso ejecutar tareas en paralelo en varias CPU.

  • Divide y vencerás.
    Una tarea se debe dividir en subtareas. Debe intentar identificar aquellas subtareas que se puedan ejecutar en paralelo.

  • Puede ser difícil ejecutar algunos problemas como tareas paralelas.

  • Algunos problemas son más sencillos. Los servidores que soportan varios clientes pueden usar una tarea independiente para manejar cada cliente.

  • Se debe tener cuidado con el hardware. La programación de demasiadas tareas paralelas puede afectar de forma negativa al rendimiento.

Recuento de CPU

Si las tareas requieren muchos cálculos, al contrario de operaciones que generan muchas E/S, el número de tareas paralelas no debe superar en gran cantidad el número de procesadores del sistema.

Puede detectar el número de procesadores de forma sencilla en Java:

int count = Runtime.getRuntime().availableProcessors();

Sin paralelismo

Los sistemas modernos contienen varias CPU. Si no aprovechan los threads de alguna forma, sólo se utilizará una parte de la potencia de procesamiento del sistema.

Definición de etapa

Si tiene una gran cantidad de datos que procesar pero solo un thread para procesar dichos datos, se utilizará una CPU.

Como ejemplo el procesamiento de una matriz podría ser una tarea simple, como buscar el valor más alto en la matriz. Entonces, en un sistema de cuatro CPU, debe haber tres CPU inactivas mientras se procesa la matriz.

Paralelismo naive

Una solución paralela simple divide los datos que se van a procesar en varios juegos en un juego de datos para cada CPU y un thread para procesar cada juego de datos.

División de datos

Siguiendo el ejemplo de la matriz (como gran juego) se divide en cuatro subjuegos de datos, uno para cada CPU.

Ello implica que se crea un thread por CPU para procesar los datos.

Tras el procesamineto de los subjuegos de datos, los resultados se tendrán que combinar de una forma significativa.

Hay distintas forma de subdividir el juego de datos grande que se va a procesar.

  • Se usaría demasiada memoria para crear una matriz por thread que contenga una copia de una parte de la matriz original.

  • Cada matriz puede compartir una referencia a una única matriz grande pero solo acceder a un subjuego de una forma con protección de hread no bloqueante.

OCP7 11 – Hilos (04) – ReentrantReadWriteLock

Nota: Los ejemplos mostrados en este artículo suponen la utilización de arquitecturas de CPU que soportan operaciones de definición y comparación atómicas (operaciones Compare And Swap), como por ejemplo los procesadores x86 o Sparc actuales. En este caso las operaciones lock / unlock serán operaciones no bloqueantes. Otras arquitecturas que no soporten esta funcionalidad pueden requerir alguna forma de bloqueo interno por parte de la plataforma.

Paquete java.util.concurrent.locks

El paquete java.util.concurrent.locks es un marco para bloquear y esperar condiciones que es distinto de las supervisiones y sincronización incorporadas.

Bloqueo de varios lectores y un único escritor

    public class ShoppingCart {
    private final ReentrantReadWriteLock rw1 = new ReentrantReadWriteLock();

    public void addItem (Object o) {

    rw1.writeLock().lock();
    //  source code to modify shopping cart
    rw1.writeLock().unlock();
    }
    }  // End class ShoppingCart

El código fuente en main() que implementa el bloqueo y el desbloqueo es el siguiente junto con el código fuente para realizar las modificaciones del shopping cart:

    rw1.writeLock().lock();
    //  source code to modify shopping cart
    rw1.writeLock().unlock();

realizando el bloqueo de escritura.

Describiendo un poco todo lo documentado, una de la funciones del paquete java.util.concurrent.locks es la implantación de un bloqueo de varios lectores con un único escritor.

Es posible que un thread no tenga ni obtenga un bloqueo de lectura mientras está en uso el bloqueo de escritura.

Varios threads pueden adquirir simultáneamente el bloqueo de lectura pero sólo uno de ellos puede adquirir el bloqueo de escritura (n -lecturas <-> 1 escritura).

El bloqueo es reentrante: un thread que ya adquirido el bloqueo de escritura puede llamar a métodos adicionales que también obtengan el bloqueo de escritura sin miedo a que se produzca un bloqueo.

Bloqueo de lectura (sin ningún escritor)

Ejemplo dónde se muestra como los métodos de lectura son concurrentes:

    public class ShoppingCart {
        public String getSummary() {
            String s = "";
            rw1.readLock().lock();
            //  Source code to read cart, modify s
            rw1.readLock().unlock();
            return s;
        }

        //  Todos los métodos de sólo lectura se pueden ejecutar de forma simultánea
        public double getTotal () {
            //  another read-only method
        }
    }   //  End class ShoppingCart

En el ejemplo todos los métodos determinados como de sólo lectura pueden agregar el código necesario para bloquear y desbloquear un bloqueo de lectura.

ReentrantReadWriteLock permite la ejecución simultánea de ambos, dónde puede ejecutar un único método de sólo lectura o varios métodos de sólo lectura concurrentes.

OCP7 11 – Hilos (05) – Variables atómicas y bloqueos de sincronización

En este artículo se exponen los mecanismos básicos que proporciona la plataforma estándar de Java para el acceso y actualización de variables de forma concurrente.
Se tratarán los siguientes conceptos:
  • Uso de variables atómicas
  • Acceso a variables mediante bloqueos de sincronización

Seguir leyendo OCP7 11 – Hilos (05) – Variables atómicas y bloqueos de sincronización

OCP7 11 – Hilos (06) – Colecciones con protección de Thread

En general las colecciones de java.util no tienen protección de thread.  Para poder utilizar colecciones en modo de protección de thread se debe utilizar uno de los siguientes mecanismos:

  • Utilizar bloques de código sincronizado para todos los accesos a una colección si se realizan escrituras.
  • Crear un envoltorio sincronizado mediante métodos de biblioteca  como java.util.Collections.synchronizedList(List<T>). Es importante destacar que el hecho de que una Collection se cree con protección thread no hace que sus elementos dispongan de la misma protección de thread.
  • Utilizar colecciones dentro de java.util.concurrent.

Recopilaciones simultáneas(ConcurrentLinkedQueue)

La clase ConcurrentLinkedQueue proporciona una cola FIFO no bloqueante con protección de thread escalable eficaz.

Adicionalmente existen cinco implementaciones en java.util.concurrent que también soportan la interfaz ampliada BlockingQueue que define las versiones de bloqueo de colocación y captura:

Ademas de las colas, este paquete proporciona implementaciones de Collection diseñadas para su uso  en contextos multihilo siguientes:

Cuándo  se espera que muchos threads accedan a la colección proporcionada, normalmente se prefiere

  • ConcurrentHashMap a HashMap sincronizado.
  • ConcurrentSkipListMap a TreeMap sincronizado.

Cuándo el número esperado de lecturas y transversales supera en gran medida el número de actualizaciones en una lista se prefiere

  • CopyOnWriteArrayList a ArrayList sincronizado .

 

OCP7 11 – Hilos (02) – Control de Errores Inesperados

La clase Thread cuenta con un mecanismo de control de errores para casos en que se produzca un final inesperado a la ejecución de éste. A través del método setUncaughtExceptionHandler de la clase Thread es posible recoger la causa de esta finalización anómala dentro del hilo principal de la aplicación y actuar en consecuencia.

A continuación se añade un ejemplo que ilustra su funcionamiento:

// Resto del código
Thread t = new Thread(new Runnable()) {

 @Override
 public void run() {
  // Código del thread que provoca
  // una finalización anómala en su
  // ejecución
 }
});
t.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){
  
 @Override
 public void uncaughtException(Thread t, Throwable e) {
  // Tratamiento del error producido en el Thread.
  // Por ejemplo, se puede registrar un error o emprender
  // una acción alternativa
 }
});

// Inicialización de la ejecución del Thread.
// Gracias al Listenr del thread no es necesario supervisar la finalización
// del thread para tratar el error pudiendo seguir la ejecución del programa
// principal
t.start(); 

// Resto del códigox

 

OCP7 08 – Aserciones

La aserción es un mecanismo que permite comprobar suposiciones en el código que ayudan a confirmar el buen funcionamiento del mismo y que este está libre de errores. En el siguiente post se muestra su funcionamiento básico y las situaciones en las que es apropiado su uso y en las que no.
Una expresión de tipo aserción se identifica por la palabra clave assert. Su sintaxis es la siguiente:
assert <expresión> [:]
Dónde:
  • expresión: Expresión booleana que indicará si la suposición se cumple o no. En caso de que no se cumpla, se lanzará un error de tipo AssertionError
  • valor: Opcional. Si se indica un valor en la expresión, este será adjuntado en el error AssertionError producido.

Seguir leyendo OCP7 08 – Aserciones

OCP7 11 – Hilos (03) – Mecanismos básicos en la plataforma Java

En este artículo se exponen los mecanismos básicos que proporciona la plataforma estándar de Java para la implementación de tareas concurrentes detallada para su versión 7. 
Se tratarán los siguientes conceptos:
  • Implementación de tareas mediante hilos de ejecución
  • Gestión del ciclo de vida de los hilos de ejecución mediante API

Seguir leyendo OCP7 11 – Hilos (03) – Mecanismos básicos en la plataforma Java