Concurrencia en Java: j.u.c y sus mecanismos de bloqueo

La paquete java.util.concurrent, abreviado comúnmente como j.u.c, es un componente fundamental de la programación concurrente en Java. Una inspección de su API revela que se compone de tres módulos principales.

Mecanismos de bloqueo

En el desarrollo concurrente con Java, existen dos enfoques para implementar bloqueos:

  1. Bloqueo intrínseco (implícito): Basado en la palabra clave synchronized. Es un mecanismo integrado en la JVM que gestiona automáticamente la adquisición y liberación del bloqueo.
  2. Bloqueo explícito: Implementado mediante la interfaz Lock del paquete j.u.c. Ofrece mayor flexibilidad, pero requiere la adquisición y liberación manual del bloqueo por parte del programador.

Componentes principales de j.u.c

  • locks: Implementaciones de bloqueos explícitos (mutex y lectura-escritura).
  • atomic: Clases de variables atómicas, base para agloritmos de colas no bloqueantes, construidas sobre CAS (Compare-And-Swap).
  • executor: Framework para la gestión de pools de hilos.
  • collections: Colecciones thread-safe (contenedores concurrentes).
  • tools: Utilidades de sincronización como semáforos, cierres (latches) y barreras (barriers).

ReentrantLock

ReentrantLock es un bloqueo reentrante y no bloqueante. Es funcionalmente similar a synchronized, pero ofrece mecanismos más poderosos y flexibles, reduciendo el potencial de interbloqueos. Soporta tanto bloqueos justos (fair) como no justos. Su núcleo se basa en el AQS (AbstractQueuedSynchronizer).

Internamente, un ReentrantLock opera sobre una cola de espera y mantiene el estado de bloqueo mediante un entero (state) que cuenta las adquisiciones, junto con una referencia al hilo que posee actualmente el bloqueo.

// Conceptualización de la estructura interna
private final Sincronizador sincro;

abstract class Sincronizador extends AbstractQueuedSynchronizer {}
// En el constructor, por defecto se crea un bloqueo no justo
public ReentrantLock() {
    this.sincro = new SincronizadorNoJusto();
}
// Dos implementaciones internas
static class SincronizadorNoJusto extends Sincronizador {};
static class SincronizadorJusto extends Sincronizador {};

Interfaz Condition

Condition se utiliza junto con un Lock para implementar el patrón de esperar/notificar. Proporciona una cola de condiciones más flexible que los métodos wait() y notify() de Object. Un ejemplo de uso típico es:

// Espera activa hasta que se cumple la condición
while (!condicionRequerida) {
    condicion.await(); // Bloquea el hilo actual
}
// Realiza la acción cuando la condición se cumple
ejecutarAccion();

ReentrantReadWriteLock

Este bloqueo mantiene un par de bloqueos: uno para lectura y otro para escritura. Al separar estos modos de acceso, la concurrencia mejora significativamente:

  • Lectura concurrente: Múltiples hilos pueden aqduirir el bloqueo de lectura simultáneamente.
  • Escritura exclusiva: Un hilo que adquiere el bloqueo de escritura bloquea a todos los demás lectores y escritores.

Sus características incluyen:

  1. Justicia: Admite modos justo y no justo.
  2. Reentrancia: Soporta hasta 65535 bloqueos recursivos de lectura y 65535 de escritura.
  3. Degradación de bloqueo: Permite cambiar de un bloqueo de escritura a uno de lectura siguiendo la secuencia: adquirir escritura -> adquirir lectura -> liberar escritura.

Para representar el estado de ambos bloqueos en una sola variable entera de 32 bits, se divide en dos mitades de 16 bits.

// Representación conceptual del estado del bloqueo
public class BloqueoLecturaEscritura implements ReadWriteLock {
    private final BloqueoLectura bloqueoLector;
    private final BloqueoEscritura bloqueoEscritor;
}

// Funciones para extraer el estado de lectura/escritura
// Los 16 bits superiores: conteo de lectores
// Los 16 bits inferiores: conteo de escritores (reentrancias)
static int obtenerConteoLectores(int estado) {
    return estado >>> 16; // Desplaza 16 bits a la derecha
}

static int obtenerConteoEscritores(int estado) {
    return estado & 0x0000FFFF; // Máscara para los 16 bits inferiores
}

Etiquetas: Java Concurrency ReentrantLock ReadWriteLock AQS BlockingQueues

Publicado el 7-3 19:45