Ce face ConcurrentHashMap
HashMap obișnuit nu este sigur când mai multe fire scriu în el odată: se pot pierde valori sau structura internă se poate strica. ConcurrentHashMap implementează aceeași interfață Map, dar permite citiri și scrieri simultane din mai multe fire.
O singură operație (put, get, remove) este sigură, dar o secvență ca „citește, apoi scrie” nu este: între cele două pași, alt fir poate schimba valoarea. Pentru astfel de actualizări folosește metodele atomice: merge, compute, computeIfAbsent, putIfAbsent.
Spre deosebire de HashMap, nu acceptă chei sau valori null. Ordinea elementelor nu este garantată; pentru afișare sortată copiază-l într-un TreeMap.
Ce returnează
Un Map sigur pentru mai multe fire.
Exemple
Exemplul 1: Mai multe fire adaugă în aceeași hartă
Patru fire adaugă câte 250 de chei diferite. Toate cele 1000 de chei ajung în hartă.
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
public static void main(String[] args) {
Map<Integer, String> comenzi = new ConcurrentHashMap<>();
try (ExecutorService executor = Executors.newFixedThreadPool(4)) {
for (int fir = 0; fir < 4; fir++) {
int start = fir * 250;
executor.submit(() -> {
for (int i = start; i < start + 250; i++) {
comenzi.put(i, "comanda-" + i);
}
});
}
}
System.out.println("Comenzi salvate: " + comenzi.size());
System.out.println("Comanda 777: " + comenzi.get(777));
}
}
Rezultat
Comenzi salvate: 1000
Comanda 777: comanda-777
Exemplul 2: computeIfAbsent: o listă pe fiecare oraș
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
public class Main {
public static void main(String[] args) {
Map<String, List<String>> clientiPeOras = new ConcurrentHashMap<>();
String[][] clienti = {{"Ana", "Iași"}, {"Mihai", "Cluj"}, {"Ioana", "Iași"}, {"Radu", "Brașov"}};
for (String[] c : clienti) {
clientiPeOras.computeIfAbsent(c[1], oras -> new CopyOnWriteArrayList<>()).add(c[0]);
}
System.out.println(new TreeMap<>(clientiPeOras));
}
}
Rezultat
{Brașov=[Radu], Cluj=[Mihai], Iași=[Ana, Ioana]}
Exemplul 3: null nu este permis
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class Main {
public static void main(String[] args) {
Map<String, String> simplu = new HashMap<>();
simplu.put("telefon", null);
System.out.println("HashMap acceptă null: " + simplu);
Map<String, String> concurent = new ConcurrentHashMap<>();
try {
concurent.put("telefon", null);
} catch (NullPointerException e) {
System.out.println("ConcurrentHashMap aruncă NullPointerException");
}
}
}
Rezultat
HashMap acceptă null: {telefon=null}
ConcurrentHashMap aruncă NullPointerException
Greșeli frecvente
- Scrii
harta.put(k, harta.get(k) + 1)din mai multe fire. Două fire pot citi aceeași valoare și o actualizare se pierde; foloseștemerge(k, 1, Integer::sum). - Folosești
Collections.synchronizedMappentru multe fire. Blochează toată harta la fiecare operație;ConcurrentHashMapeste mai rapid.
Comenzi înrudite
ConcurrentHashMap.merge: Actualizează atomic valoarea unei chei: merge adaugă sau combină (contoare), compute calculează valoarea nouă din cea veche.AtomicInteger: Un int pe care mai multe fire îl pot modifica sigur, fără synchronized: get(), set(), addAndGet() și altele.ReentrantLock: Un lacăt: lock() lasă un singur fir să intre în zona protejată, unlock() îl eliberează; unlock se pune mereu în finally.ExecutorService: Un grup de fire de execuție care primește sarcini și le rulează; se oprește cu shutdown() și se așteaptă cu awaitTermination().