Sobes.tech
Middle

¿Qué puedes contarme sobre las colecciones multihilo en Java?

sobes.tech AI

Respuesta de la IA

Las colecciones concurrentes en Java proporcionan formas seguras de trabajar con datos desde múltiples hilos simultáneamente, previniendo condiciones de carrera y otros problemas de sincronización.

Las interfaces y clases principales incluyen:

  • ConcurrentMap: Extiende Map, asegurando operaciones atómicas.
    • ConcurrentHashMap: Implementación de alto rendimiento que no bloquea toda la mapa durante modificaciones. Proporciona seguridad en hilos sin bloqueo completo.
  • CopyOnWriteArrayList y CopyOnWriteArraySet: Colecciones seguras para hilos que crean una copia del array base en cada modificación. Son adecuadas para colecciones que se leen frecuentemente y se modifican raramente. La lectura ocurre sin bloqueo.
  • BlockingQueue: Interfaz de cola que soporta operaciones de espera hasta que la cola esté vacía o llena. Se usa para organizar la interacción entre hilos, por ejemplo, en el patrón productor-consumidor.
    • ArrayBlockingQueue: Implementación de BlockingQueue basada en array con capacidad fija.
    • LinkedBlockingQueue: Implementación de BlockingQueue basada en lista enlazada con capacidad opcional.
    • SynchronousQueue: Cola con capacidad cero. Cada operación de inserción espera a una operación de extracción correspondiente y viceversa.
  • ConcurrentLinkedQueue: Implementación segura para hilos de Queue basada en lista enlazada. No soporta operaciones de bloqueo.

Diferencias con colecciones sincronizadas (Collections.synchronizedList, Collections.synchronizedMap, etc.):

  • Las colecciones sincronizadas usan bloqueo completo a nivel de objeto, lo que puede reducir el rendimiento en alta competencia.
  • Las colecciones concurrentes, como ConcurrentHashMap, usan bloqueo más granular u otros mecanismos para lograr mejor rendimiento.
  • Los iteradores de colecciones sincronizadas pueden lanzar ConcurrentModificationException si la colección se modifica durante la iteración desde otro hilo. Los iteradores de colecciones concurrentes suelen ser más resistentes a estos cambios.

La elección de una colección concurrente específica depende del escenario de uso: frecuencia de lectura/escritura, necesidad de operaciones de bloqueo, requisitos de rendimiento, etc.

Ejemplo de uso de ConcurrentHashMap:

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentExample {

    private final ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

    public void updateValue(String key, Integer value) {
        map.put(key, value); // Operación segura para hilos
    }

    public Integer getValue(String key) {
        return map.get(key); // Operación segura para hilos
    }

    public void incrementValue(String key) {
        // Operación atómica: si la clave no existe, inserta 1, si existe, incrementa el valor existente
        map.compute(key, (k, v) -> (v == null) ? 1 : v + 1);
    }
}

Ejemplo de uso de BlockingQueue:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ArrayBlockingQueue;

public class ProducerConsumer {

    private final BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);

    // Productor
    public void produce(Integer item) throws InterruptedException {
        queue.put(item); // Se bloquea si la cola está llena
        System.out.println("Produced: " + item);
    }

    // Consumidor
    public Integer consume() throws InterruptedException {
        Integer item = queue.take(); // Se bloquea si la cola está vacía
        System.out.println("Consumed: " + item);
        return item;
    }
}