1. The Hook (The "Byte-Sized" Intro)
In a Nutshell: Concurrent collections (java.util.concurrent) are thread-safe without external synchronization. ConcurrentHashMap uses lock striping (multiple locks for segments)—allows concurrent reads/writes. CopyOnWriteArrayList clones array on modification—ideal for read-heavy scenarios. BlockingQueue implementations (ArrayBlockingQueue, LinkedBlockingQueue) block on empty/full—perfect for producer-consumer. Never use synchronized wrappers (Collections.synchronizedMap) for high concurrency!
Think of library. Regular HashMap = one checkout desk (bottleneck). ConcurrentHashMap = multiple checkout desks (parallel service). CopyOnWriteArrayList = photocopied catalog (read anytime, update makes new copy)!
2. Conceptual Clarity (The "Simple" Tier)
💡 The Analogy
- HashMap: Single library (thread-unsafe)
- Collections.synchronizedMap: Library with one door lock (slow)
- ConcurrentHashMap: Library with section locks (fast, concurrent)
3. Technical Mastery (The "Deep Dive")
ConcurrentHashMap vs HashMap
// ❌ NOT thread-safe
Map<String, Integer> map = new HashMap<>();
// ✅ Thread-safe but slow (one lock)
Map<String, Integer> map = Collections.synchronizedMap(new HashMap<>());
// ✅ Thread-safe and fast (lock striping)
Map<String, Integer> map = new ConcurrentHashMap<>();4. Interactive & Applied Code
import java.util.concurrent.*;
import java.util.*;
public class ConcurrentCollectionsDemo {
public static void main(String[] args) throws Exception {
demonstrateConcurrentHashMap();
demonstrateCopyOnWriteArrayList();
demonstrateBlockingQueue();
}
static void demonstrateConcurrentHashMap() throws Exception {
System.out.println("=== CONCURRENT HASHMAP ===");
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// Atomic operations
map.put("A", 1);
map.putIfAbsent("A", 2); // Won't replace
System.out.println("Value: " + map.get("A")); // 1
// Atomic compute
map.compute("A", (k, v) -> v + 10); // 1 + 10 = 11
System.out.println("After compute: " + map.get("A"));
// Concurrent modification safe
ExecutorService executor = Executors.newFixedThreadPool(3);
for (int i = 0; i < 100; i++) {
final int val = i;
executor.submit(() -> map.put("Key" + val, val));
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.SECONDS);
System.out.println("Map size: " + map.size());
}
static void demonstrateCopyOnWriteArrayList() {
System.out.println("\n=== COPY ON WRITE ARRAY LIST ===");
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// Iterator never throws ConcurrentModificationException
for (String item : list) {
System.out.println(item);
list.add("D"); // Safe during iteration!
// But iterator won't see "D" (snapshot)
}
System.out.println("Final list: " + list);
// Use case: Event listeners (mostly reads, rare writes)
List<Runnable> listeners = new CopyOnWriteArrayList<>();
listeners.add(() -> System.out.println("Listener 1"));
listeners.add(() -> System.out.println("Listener 2"));
// Fire events (concurrent reads)
listeners.forEach(Runnable::run);
}
static void demonstrateBlockingQueue() throws Exception {
System.out.println("\n=== BLOCKING QUEUE ===");
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(5);
// Producer
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
queue.put(i); // Blocks if queue full
System.out.println("Produced: " + i);
Thread.sleep(100);
}
} catch (InterruptedException e) {}
});
// Consumer
Thread consumer = new Thread(() -> {
try {
Thread.sleep(500); // Start slower
for (int i = 1; i <= 10; i++) {
Integer item = queue.take(); // Blocks if queue empty
System.out.println("Consumed: " + item);
Thread.sleep(200);
}
} catch (InterruptedException e) {}
});
producer.start();
consumer.start();
producer.join();
consumer.join();
}
}
// Comparison example
class CollectionPerformanceTest {
public static void main(String[] args) throws Exception {
int THREADS = 10;
int OPERATIONS = 10000;
// Test ConcurrentHashMap
Map<Integer, Integer> concurrentMap = new ConcurrentHashMap<>();
long start = System.currentTimeMillis();
runTest(concurrentMap, THREADS, OPERATIONS);
long concurrentTime = System.currentTimeMillis() - start;
// Test synchronized HashMap
Map<Integer, Integer> syncMap = Collections.synchronizedMap(new HashMap<>());
start = System.currentTimeMillis();
runTest(syncMap, THREADS, OPERATIONS);
long syncTime = System.currentTimeMillis() - start;
System.out.println("ConcurrentHashMap: " + concurrentTime + "ms");
System.out.println("Synchronized Map: " + syncTime + "ms");
// ConcurrentHashMap is typically 2-3x faster!
}
static void runTest(Map<Integer, Integer> map, int threads, int ops) throws Exception {
ExecutorService executor = Executors.newFixedThreadPool(threads);
for (int i = 0; i < ops; i++) {
final int key = i;
executor.submit(() -> map.put(key, key));
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
}
}5. The Comparison & Decision Layer
| Collection | Reads | Writes | Use When |
|---|---|---|---|
| ConcurrentHashMap | Fast | Fast | High concurrency |
| CopyOnWriteArrayList | Very fast | Slow | Read-heavy |
| BlockingQueue | Blocking | Blocking | Producer-consumer |
| ConcurrentLinkedQueue | Lock-free | Lock-free | High throughput |
6. The "Interview Corner" (The Edge)
The "Killer" Interview Question: "Why can't you use HashMap.size() reliably with concurrent modifications?" Answer: size() is not atomic! With ConcurrentHashMap, size() is approximate during concurrent modifications:
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
// Thread 1 adds items
// Thread 2 calls size()
// size() may return stale value!
// Solution: Use mappingCount() for accurate count
long count = map.mappingCount(); // More accuratePro-Tip: When to use CopyOnWriteArrayList:
// ✅ GOOD: Event listeners (rare modifications)
List<Listener> listeners = new CopyOnWriteArrayList<>();
listeners.add(listener); // Rare
listeners.forEach(l -> l.onEvent()); // Frequent
// ❌ BAD: Frequent modifications
List<String> cache = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10000; i++) {
cache.add("Item" + i); // Copies array 10,000 times!
}
// Use ConcurrentHashMap or synchronizedList insteadRule: CopyOnWriteArrayList only if reads >> writes (10:1 ratio minimum)!