Concurrent Collections
Thread-safe collections from java.util.concurrent.
At a Glance
ConcurrentHashMap — General-purpose thread-safe map. Lock-free reads, per-bucket locking on writes. High throughput under contention. No nulls allowed. Default choice when you need a shared map. ConcurrentSkipListMap — Sorted concurrent map (like a thread-safe TreeMap). O(log n) operations via lock-free CAS. Use when you need sorted keys with concurrent access. Higher overhead than ConcurrentHashMap for unsorted workloads. ConcurrentSkipListSet — Sorted concurrent set backed by ConcurrentSkipListMap. Use when you need a thread-safe TreeSet. ConcurrentHashMap.newKeySet() — Concurrent unordered set backed by ConcurrentHashMap. Use when you need a thread-safe HashSet. CopyOnWriteArrayList — Every write copies the entire backing array. Reads are lock-free and fast. Use for listener/observer lists or configs that are read often and rarely modified. Writes are O(n) and expensive at scale. CopyOnWriteArraySet — Set backed by CopyOnWriteArrayList. Same tradeoffs: great for small, read-heavy sets. contains() is O(n). ConcurrentLinkedQueue — Unbounded lock-free FIFO queue. Use for high-throughput producer-consumer when you don't need blocking. size() is O(n). ConcurrentLinkedDeque — Unbounded lock-free double-ended queue. Use when you need both FIFO and LIFO access concurrently. ArrayBlockingQueue — Bounded, array-backed blocking queue. put() blocks when full, take() blocks when empty. Single lock means lower throughput than LinkedBlockingQueue, but bounded capacity prevents memory issues. LinkedBlockingQueue — Optionally bounded blocking queue with separate put/take locks for higher throughput. Default choice for bounded producer-consumer. Unbounded by default — set a capacity to avoid memory leaks. PriorityBlockingQueue — Unbounded blocking queue ordered by priority (heap). Use for priority-based task scheduling. Cannot be bounded — producers never block. SynchronousQueue — Zero-capacity direct handoff. put() blocks until another thread calls take(). Used by Executors.newCachedThreadPool(). No buffering — tight coupling between producer and consumer. DelayQueue — Unbounded queue where elements become available only after their delay expires. Use for scheduled retries, cache expiration, rate limiting. Elements must implement Delayed. LinkedTransferQueue — Unbounded FIFO queue that combines SynchronousQueue semantics (transfer() blocks until consumed) with normal queue buffering. Most flexible blocking queue — use when you sometimes want direct handoff and sometimes want buffering. LinkedBlockingDeque — Optionally bounded blocking double-ended queue. Use for work-stealing algorithms where threads take from their own end and steal from the other.
ConcurrentHashMap
The workhorse concurrent map. High throughput under contention.
| Operation | Complexity | Notes |
get | O(1) | Lock-free read via volatile. |
put / remove | O(1) | Locks only the affected bin (bucket). |
putIfAbsent / computeIfAbsent | O(1) | Atomic check-and-set. |
size() | O(n) | Approximate under concurrency. Use mappingCount() for long return. |
Key Differences from HashMap
| HashMap | ConcurrentHashMap |
| Thread-safe | No | Yes |
| Null keys/values | Allowed | Not allowed |
| Iteration | Fail-fast (throws ConcurrentModificationException) | Weakly consistent (no exception) |
| Atomic ops | None built-in | putIfAbsent, compute, merge |
ConcurrentMap<String, Integer> counters = new ConcurrentHashMap<>();
// Atomic increment
counters.merge("hits", 1, Integer::sum);
// Atomic compute
counters.compute("hits", (k, v) -> v == null ? 1 : v + 1);
// putIfAbsent — returns existing value if key present
counters.putIfAbsent("misses", 0);
// Bulk operations (parallel with threshold)
counters.forEach(1000, (k, v) ->
System.out.println(k + " = " + v));
long total = counters.reduceValues(1000, Integer::sum);
// As a concurrent Set
Set<String> set = ConcurrentHashMap.newKeySet();
ConcurrentSkipListMap & ConcurrentSkipListSet
Concurrent sorted collections. The thread-safe equivalent of TreeMap / TreeSet.
| Operation | Complexity | Notes |
get / put / remove | O(log n) | Lock-free via CAS. Probabilistic skip-list structure. |
firstKey / lastKey | O(log n) | Navigable — supports headMap, tailMap, subMap. |
ConcurrentNavigableMap<String, Integer> map = new ConcurrentSkipListMap<>();
map.put("banana", 2);
map.put("apple", 5);
map.put("cherry", 3);
map.firstKey(); // "apple"
map.headMap("cherry"); // {apple=5, banana=2}
NavigableSet<String> set = new ConcurrentSkipListSet<>();
set.add("B");
set.add("A");
set.first(); // "A"
Copy-on-Write Collections
Best for read-heavy workloads with infrequent writes. Every mutation copies the entire array.
| Class | Read | Write | Iteration | When to Use |
CopyOnWriteArrayList | O(1) | O(n) | Snapshot — never throws CME | Listener lists, config lists, observer patterns. |
CopyOnWriteArraySet | O(n) | O(n) | Snapshot — never throws CME | Small sets with rare writes (backed by CopyOnWriteArrayList). |
List<EventListener> listeners = new CopyOnWriteArrayList<>();
listeners.add(myListener);
// Safe iteration — iterates over a snapshot
for (EventListener l : listeners) {
l.onEvent(event); // no ConcurrentModificationException
}
// Writes are expensive (copies entire array)
listeners.remove(myListener);
Concurrent (Non-Blocking) Queues
Lock-free queues for high-throughput producer-consumer scenarios without blocking.
| Class | Type | Bounded? | Notes |
ConcurrentLinkedQueue | FIFO queue | Unbounded | Lock-free via CAS. size() is O(n). |
ConcurrentLinkedDeque | Double-ended queue | Unbounded | Lock-free. Supports both FIFO and LIFO. |
Queue<Task> tasks = new ConcurrentLinkedQueue<>();
// Producer
tasks.offer(new Task("process-order"));
// Consumer (non-blocking — returns null if empty)
Task t = tasks.poll();
Blocking Queues
Queues that block on put() when full and take() when empty. Core building block for producer-consumer patterns.
| Class | Bounded? | Ordering | Locks | Best For |
ArrayBlockingQueue | Yes (fixed) | FIFO | 1 lock | Bounded producer-consumer. Fairness option. |
LinkedBlockingQueue | Optional (default unbounded) | FIFO | 2 locks (put/take) | Higher throughput than ArrayBlockingQueue. |
PriorityBlockingQueue | Unbounded | Priority (heap) | 1 lock | Priority-based task scheduling. |
SynchronousQueue | 0 capacity | N/A — direct handoff | Lock-free (dual stack/queue) | Direct handoff between threads. Used by Executors.newCachedThreadPool(). |
DelayQueue | Unbounded | By delay expiration | 1 lock | Scheduled tasks, cache expiration, rate limiting. |
LinkedTransferQueue | Unbounded | FIFO | Lock-free (dual queue) | Like SynchronousQueue + LinkedBlockingQueue. Supports transfer(). |
BlockingQueue API
| Action | Throws | Returns special value | Blocks | Times out |
| Insert | add(e) | offer(e) | put(e) | offer(e, time, unit) |
| Remove | remove() | poll() | take() | poll(time, unit) |
| Examine | element() | peek() | N/A | N/A |
// Bounded producer-consumer
BlockingQueue<Task> queue = new ArrayBlockingQueue<>(100);
// Producer thread
queue.put(new Task("job-1")); // blocks if full
// Consumer thread
Task t = queue.take(); // blocks if empty
// With timeout
Task t2 = queue.poll(5, TimeUnit.SECONDS);
// SynchronousQueue — direct handoff
BlockingQueue<Task> handoff = new SynchronousQueue<>();
// put() blocks until another thread calls take()
// DelayQueue — elements available only after delay expires
DelayQueue<DelayedTask> delayed = new DelayQueue<>();
delayed.put(new DelayedTask("retry", 30, TimeUnit.SECONDS));
LinkedBlockingDeque
A blocking double-ended queue. Useful for work-stealing algorithms.
BlockingDeque<Task> deque = new LinkedBlockingDeque<>(100);
deque.putFirst(highPriority); // blocks if full
deque.putLast(normalTask);
Task t = deque.takeFirst(); // blocks if empty
Task t2 = deque.takeLast(); // steal from other end
Choosing the Right Collection
| Scenario | Use |
| General-purpose concurrent map | ConcurrentHashMap |
| Sorted concurrent map | ConcurrentSkipListMap |
| Concurrent set | ConcurrentHashMap.newKeySet() or ConcurrentSkipListSet |
| Read-heavy list/set, rare writes | CopyOnWriteArrayList / CopyOnWriteArraySet |
| Bounded producer-consumer | ArrayBlockingQueue or LinkedBlockingQueue |
| Direct thread-to-thread handoff | SynchronousQueue |
| Delayed/scheduled elements | DelayQueue |
| Priority-based concurrent queue | PriorityBlockingQueue |
| High-throughput non-blocking queue | ConcurrentLinkedQueue |
| Work-stealing deque | LinkedBlockingDeque |