Sobes.tech
Middle

O que pode contar sobre coleções multithread em Java?

sobes.tech IA

Resposta da IA

As coleções concorrentes em Java fornecem formas seguras de trabalhar com dados de múltiplos threads simultaneamente, prevenindo condições de corrida e outros problemas de sincronização.

As principais interfaces e classes incluem:

  • ConcurrentMap: Estende Map, garantindo operações atômicas.
    • ConcurrentHashMap: Implementação de alto desempenho que não bloqueia toda a mapa durante modificações. Garante segurança em múltiplos threads sem bloqueio completo.
  • CopyOnWriteArrayList e CopyOnWriteArraySet: Coleções seguras para threads que criam uma cópia do array base em cada modificação. São adequadas para coleções que são lidas frequentemente e modificadas raramente. A leitura ocorre sem bloqueio.
  • BlockingQueue: Interface de fila que suporta operações de espera até que a fila esteja vazia ou cheia. Usada para organizar a interação entre threads, por exemplo, no padrão produtor-consumidor.
    • ArrayBlockingQueue: Implementação de BlockingQueue baseada em array com capacidade fixa.
    • LinkedBlockingQueue: Implementação de BlockingQueue baseada em lista ligada com capacidade opcional.
    • SynchronousQueue: Fila com capacidade zero. Cada operação de inserção espera por uma operação de extração correspondente e vice-versa.
  • ConcurrentLinkedQueue: Implementação segura para threads de Queue baseada em lista ligada. Não suporta operações de bloqueio.

Diferenças com coleções sincronizadas (Collections.synchronizedList, Collections.synchronizedMap, etc.):

  • Coleções sincronizadas usam bloqueio completo ao nível do objeto, o que pode reduzir o desempenho em alta concorrência.
  • Coleções concorrentes, como ConcurrentHashMap, usam bloqueio mais granular ou outros mecanismos para alcançar melhor desempenho.
  • Os iteradores de coleções sincronizadas podem lançar ConcurrentModificationException se a coleção for modificada durante a iteração de outro thread. Os iteradores de coleções concorrentes geralmente são mais resistentes a essas mudanças.

A escolha de uma coleção concorrente específica depende do cenário de uso: frequência de leitura/escrita, necessidade de operações de bloqueio, requisitos de desempenho, etc.

Exemplo 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); // Operação segura para threads
    }

    public Integer getValue(String key) {
        return map.get(key); // Operação segura para threads
    }

    public void incrementValue(String key) {
        // Operação atômica: se a chave não existir, insere 1, caso contrário, incrementa o valor existente
        map.compute(key, (k, v) -> (v == null) ? 1 : v + 1);
    }
}

Exemplo 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);

    // Produtor
    public void produce(Integer item) throws InterruptedException {
        queue.put(item); // Bloqueia se a fila estiver cheia
        System.out.println("Produced: " + item);
    }

    // Consumidor
    public Integer consume() throws InterruptedException {
        Integer item = queue.take(); // Bloqueia se a fila estiver vazia
        System.out.println("Consumed: " + item);
        return item;
    }
}