Careeroza
ConcurrentHashMap and Atomic Classes
Thread-safe Collections
| Need | Use | Instead of |
|---|---|---|
| Shared map | ConcurrentHashMap | HashMap (unsafe), Hashtable/synchronizedMap (one big lock) |
| Read-mostly list (listeners) | CopyOnWriteArrayList | synchronizedList |
| Producer–consumer queue | ArrayBlockingQueue, LinkedBlockingQueue | wait/notify |
| Non-blocking queue | ConcurrentLinkedQueue | LinkedList |
| Sorted concurrent map | ConcurrentSkipListMap | TreeMap |
Note: ConcurrentHashMap locks only small parts (bins) and reads without locking. Use its atomic methods (merge, compute, computeIfAbsent, putIfAbsent) — a separate get then put is still a race.
Counting Page Views from Many Threads
Example
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.*;
public class Main {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, Integer> views = new ConcurrentHashMap<>();
AtomicLong totalViews = new AtomicLong();
LongAdder errors = new LongAdder(); // fastest for heavy-write counters
String[] pages = {"/home", "/cart", "/home", "/product/1", "/home"};
try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
for (int t = 0; t < 4; t++) {
pool.submit(() -> {
for (int i = 0; i < 1000; i++) {
String page = pages[i % pages.length];
views.merge(page, 1, Integer::sum); // atomic read-modify-write
totalViews.incrementAndGet();
if (i % 100 == 0) errors.increment();
}
});
}
}
System.out.println(new TreeMap<>(views));
System.out.println("Total views: " + totalViews.get());
System.out.println("Errors: " + errors.sum());
}
}Output
{/cart=800, /home=2400, /product/1=800}
Total views: 4000
Errors: 40Atomic Classes and CAS
AtomicInteger, AtomicLong, AtomicBoolean and AtomicReference use the CPU instruction compare-and-swap (CAS): "set to new value only if it is still the old value", retrying on conflict. No locks, no blocking.
Example
import java.util.concurrent.atomic.*;
public class Main {
public static void main(String[] args) {
AtomicInteger stock = new AtomicInteger(10);
System.out.println(stock.incrementAndGet()); // 11
System.out.println(stock.getAndAdd(5)); // returns 11, now 16
System.out.println(stock.compareAndSet(16, 0)); // true: was 16, set to 0
System.out.println(stock.compareAndSet(16, 99)); // false: value is 0
System.out.println(stock.updateAndGet(x -> x + 7)); // 7
System.out.println(stock.accumulateAndGet(3, Math::max)); // 7
AtomicReference<String> status = new AtomicReference<>("PENDING");
boolean shipped = status.compareAndSet("PENDING", "SHIPPED");
System.out.println(shipped + " " + status.get());
}
}Output
11 11 true false 7 7 true SHIPPED