Concurrent Collections

Thread-safe collections from java.util.concurrent.

At a Glance

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