Skip to content

Concurrent Collections — ConcurrentHashMap, CopyOnWriteArrayList, and BlockingQueue

DodaTech Updated 2026-06-28 6 min read

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 readsget() never blocks
  • Striped locking for writes — only locks the segment/bucket being modified
  • Weakly consistent iteratorsIterator 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

  1. Using HashMap in concurrent code. Data corruption is guaranteed. Always use ConcurrentHashMap.
  2. Iterating over Collections.synchronizedMap() without synchronization. The iterator is not thread-safe. Synchronize on the map during iteration.
  3. Using CopyOnWriteArrayList for write-heavy workloads. Every write copies the entire array — O(n) per mutation. Use ConcurrentLinkedQueue or ArrayList (with locking) instead.
  4. Calling put() on ConcurrentHashMap when putIfAbsent() or computeIfAbsent() is appropriate. Compound operations need atomic versions.
  5. Putting null in 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

What is the initial capacity of ConcurrentHashMap?

16, same as HashMap. The concurrency level (internal segmentation) is also 16 by default.

Can I use ConcurrentHashMap keys or values as null?

No. ConcurrentHashMap does not allow null keys or values. This prevents ambiguity in methods like get() that return null for both missing keys and null values.

What is a `LinkedTransferQueue`?

A concurrent queue that supports the 'transfer' mode: a producer can block until a consumer takes the element. This enables synchronous handoff within a queue structure.

What is the difference between `ConcurrentHashMap` and `synchronizedMap`?

ConcurrentHashMap uses fine-grained locking or lock-free algorithms for high concurrency. synchronizedMap uses a single lock per map — all operations block while any operation is in progress.

What is a `ConcurrentSkipListMap`?

A concurrent, sorted map (equivalent to TreeMap). It uses a skip list data structure and provides O(log n) operations with high concurrency.

Mini Project

Write a program ConcurrentCollectionsDemo.java that:

  1. Creates a shared HashMap accessed by 10 threads — demonstrates data corruption
  2. Fixes with ConcurrentHashMap — shows correct results
  3. Measures throughput: synchronizedMap vs ConcurrentHashMap with 10 threads performing 10,000 operations each
  4. Demonstrates CopyOnWriteArrayList — many readers (10 threads) and one writer
  5. Implements a producer-consumer with ArrayBlockingQueue — multiple producers/consumers
  6. Uses ConcurrentHashMap.computeIfAbsent for a simple cache
  7. Shows the difference between ConcurrentHashMap iteration 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