Concurrent Collections — ConcurrentHashMap, CopyOnWriteArrayList, and BlockingQueue
In this tutorial, you will learn about Concurrent Collections. We cover key concepts, practical examples, and best practices to help you master this topic.
Java's java.util.concurrent package provides thread-safe collections optimized for high-concurrency scenarios. Synchronizing every access with synchronized is safe but slow — concurrent collections use sophisticated lock-free algorithms and fine-grained locking to support high throughput under contention.
What You'll Learn
- ConcurrentHashMap: lock-free reads, striped locks for writes
- CopyOnWriteArrayList: snapshot-style iteration for read-heavy scenarios
- BlockingQueue implementations for producer-consumer
- Thread-safe collection wrappers
Why It Matters
Using HashMap or ArrayList in multi-threaded code causes data corruption. Concurrent collections are designed from the ground up for Thread Safety — understanding their performance characteristics helps you choose the right one.
Real-World Use
ConcurrentHashMap backs in-memory caches in web applications. CopyOnWriteArrayList stores event listeners in frameworks. BlockingQueue is the heart of thread pools.
ConcurrentHashMap
A high-performance concurrent map:
ConcurrentHashMap<String, Integer> scores = new ConcurrentHashMap<>();
scores.put("Alice", 95);
scores.put("Bob", 87);
// Safe iteration — weakly consistent
for (String name : scores.keySet()) {
System.out.println(name + ": " + scores.get(name));
}
// Atomic operations
scores.putIfAbsent("Charlie", 90); // only if not present
scores.replace("Alice", 95, 96); // only if current value is 95
scores.computeIfAbsent("Dave", k -> 0); // lazy atomic init
scores.merge("Alice", 1, Integer::sum); // atomic upsert
Characteristics
- Lock-free reads —
get()never blocks - Striped locking for writes — only locks the segment/bucket being modified
- Weakly consistent iterators — Iterator reflects the state at some point; may or may not reflect subsequent modifications
- Not
null-safe — keys and values cannot be null
Performance
// Bad: wrapping HashMap with synchronized
Map<String, Data> bad = Collections.synchronizedMap(new HashMap<>());
// Good: ConcurrentHashMap
Map<String, Data> good = new ConcurrentHashMap<>();
In read-heavy scenarios, ConcurrentHashMap can be 10x faster than synchronizedMap.
Bulk Operations (Java 8+)
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.forEach(1, (k, v) -> System.out.println(k + ": " + v));
map.reduceEntries(1, (e1, e2) -> e1.getValue() > e2.getValue() ? e1 : e2);
map.search(1, (k, v) -> v > 100 ? k : null);
CopyOnWriteArrayList
A thread-safe list where every mutation creates a new copy of the underlying array:
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.add("A");
list.add("B");
list.add("C");
// Safe iteration — iterators never throw ConcurrentModificationException
for (String s : list) {
System.out.println(s);
}
Characteristics
- Thread-safe iteration — iterators reflect the array at creation time
- Expensive writes — O(n) per add/set because it copies the entire array
- Cheap reads — O(1) get(), no locking needed
- Best for — event listener lists, rarely modified collections with many iterations
CopyOnWriteArraySet
A set backed by CopyOnWriteArrayList:
CopyOnWriteArraySet<String> set = new CopyOnWriteArraySet<>();
set.add("unique");
BlockingQueue
A queue that supports blocking operations:
BlockingQueue<String> queue = new ArrayBlockingQueue<>(100);
// Producer
queue.put("item"); // blocks if full
boolean added = queue.offer("item", 1, TimeUnit.SECONDS); // timed offer
// Consumer
String item = queue.take(); // blocks if empty
String item2 = queue.poll(1, TimeUnit.SECONDS); // timed poll
Implementations
| Implementation | Characteristics |
|---|---|
ArrayBlockingQueue |
Bounded, array-backed, fair mode optional |
LinkedBlockingQueue |
Optionally bounded, linked-node |
PriorityBlockingQueue |
Unbounded, priority-ordered |
SynchronousQueue |
Zero capacity — handoff |
LinkedTransferQueue |
Transfer mode — producer can wait for consumer |
DelayQueue |
Elements can be taken after their delay expires |
Collections.synchronizedXxx
For collections that lack concurrent counterparts:
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
Set<String> syncSet = Collections.synchronizedSet(new HashSet<>());
Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());
These are simple wrappers that synchronize every method. They are less efficient than dedicated concurrent collections and require external synchronization for iteration.
Iteration with synchronized wrappers
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
synchronized (syncList) { // must synchronize during iteration
for (String s : syncList) {
System.out.println(s);
}
}
Choosing the Right Collection
| Scenario | Recommendation |
|---|---|
| High concurrency, frequent reads/writes | ConcurrentHashMap |
| Rarely modified, frequently iterated | CopyOnWriteArrayList |
| Producer-consumer | ArrayBlockingQueue / LinkedBlockingQueue |
| Need synchronized iteration | Collections.synchronized* with explicit synchronization |
| Single-threaded or low contention | HashMap / ArrayList |
Common Mistakes
- Using
HashMapin concurrent code. Data corruption is guaranteed. Always useConcurrentHashMap. - Iterating over
Collections.synchronizedMap()without synchronization. The iterator is not thread-safe. Synchronize on the map during iteration. - Using
CopyOnWriteArrayListfor write-heavy workloads. Every write copies the entire array — O(n) per mutation. UseConcurrentLinkedQueueorArrayList(with locking) instead. - Calling
put()onConcurrentHashMapwhenputIfAbsent()orcomputeIfAbsent()is appropriate. Compound operations need atomic versions. - Putting
nullin concurrent collections. ConcurrentHashMap, ArrayBlockingQueue, and ConcurrentLinkedQueue do not allow null.
Practice Questions
1. How does ConcurrentHashMap achieve high concurrency?
It uses lock-free reads (volatile reads) and striped locking for writes — only the specific bucket being modified is locked, not the entire map.
2. What is a weakly consistent iterator?
An iterator that reflects the state of the collection at some point since it was created. It may or may not reflect subsequent modifications. It never throws ConcurrentModificationException.
3. When would you use CopyOnWriteArrayList?
For collections that are rarely modified but frequently iterated, such as event listener lists in a UI framework.
4. What is the difference between put() and offer() on BlockingQueue?
put() blocks until space is available. offer() returns false if the queue is full. offer(timeout) waits for a limited time.
5. Why must you synchronize iteration on Collections.synchronizedList()?
The wrapper only synchronizes individual methods. Iteration calls hasNext() and next() multiple times — without external synchronization, the list may be modified between calls.
Challenge Question:
Implement a thread-safe URL cache that caches HTTP responses. Use ConcurrentHashMap with computeIfAbsent for atomic loading. Add a TTL (time-to-live) mechanism that expires entries after 60 seconds. Use ScheduledExecutorService to periodically clean expired entries. Ensure reads do not block other reads.
FAQ
Mini Project
Write a program ConcurrentCollectionsDemo.java that:
- Creates a shared
HashMapaccessed by 10 threads — demonstrates data corruption - Fixes with
ConcurrentHashMap— shows correct results - Measures throughput: synchronizedMap vs ConcurrentHashMap with 10 threads performing 10,000 operations each
- Demonstrates
CopyOnWriteArrayList— many readers (10 threads) and one writer - Implements a producer-consumer with
ArrayBlockingQueue— multiple producers/consumers - Uses
ConcurrentHashMap.computeIfAbsentfor a simple cache - Shows the difference between
ConcurrentHashMapiteration and synchronizedMap iteration
What's Next
Concurrent collections handle shared data, but some problems need finer-grained parallelism. Lesson 56 covers the Fork-Join framework — RecursiveTask, RecursiveAction, work stealing, and parallel array processing.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro