BackendBench
Coffee

Unravel › Java concepts › Concurrency

Concurrency

Concurrent collections

Concurrent collections are safe to share between threads.

Learn these first

At a glance

Mental model

A shared whiteboard with rules so people can write at once without erasing each other, and a conveyor belt that makes the packer wait when it is empty.

What happens, in order
  1. 1ConcurrentHashMap locks only parts of the table, so threads rarely block each otherUse its atomic methods (putIfAbsent, computeIfAbsent, merge) for check-then-act logic.
  2. 2BlockingQueue implements the producer/consumer pattern: put waits when full, take waits when emptyA bounded queue gives natural back-pressure.
  3. 3CopyOnWriteArrayList copies the whole array on every change; it is good for rarely changed lists read very often (listener lists)
Remember
Watch out
Check-then-act with separate callsExpecting null values in ConcurrentHashMapUnbounded queuesCopyOnWriteArrayList for frequently modified data
Example (highlighted lines explain it)
private final ConcurrentHashMap<String, LongAdder> hits = new ConcurrentHashMap<>();private final BlockingQueue<Job> queue = new ArrayBlockingQueue<>(100);public void count(String page) {    hits.computeIfAbsent(page, k -> new LongAdder()).increment();}public void submit(Job job) throws InterruptedException {    queue.put(job);          // waits when the queue is full}
  1. line 1 uses ConcurrentHashMap, the core of Concurrent collections
  2. line 2 uses BlockingQueue, the core of Concurrent collections
  3. line 8 waits when the queue is full
Read the full explanation (about 1 min)

Concurrent collections are safe to share between threads. ConcurrentHashMap allows many readers and writers, and a BlockingQueue lets producer and consumer threads hand work to each other.

A mental model

A shared whiteboard with rules so people can write at once without erasing each other, and a conveyor belt that makes the packer wait when it is empty.

How it works

ConcurrentHashMap locks only parts of the table, so threads rarely block each other. Use its atomic methods (putIfAbsent, computeIfAbsent, merge) for check-then-act logic.

BlockingQueue implements the producer/consumer pattern: put waits when full, take waits when empty. A bounded queue gives natural back-pressure.

CopyOnWriteArrayList copies the whole array on every change; it is good for rarely changed lists read very often (listener lists).

Going deeper

Individually safe operations are not safe together: if (!map.containsKey(k)) map.put(k, v) is a race even on a ConcurrentHashMap; use putIfAbsent. ConcurrentHashMap does not allow null keys or values. Iterators are weakly consistent: they never throw ConcurrentModificationException but may or may not show concurrent changes.

Under the hood

Since Java 8 ConcurrentHashMap uses compare-and-swap for empty buckets and locks only the head node of a bucket for updates. Blocking queues use a lock and conditions (or CAS for the linked variants).

Common mistakes

  • Check-then-act with separate calls.
  • Expecting null values in ConcurrentHashMap.
  • Unbounded queues.
  • CopyOnWriteArrayList for frequently modified data.

How Unravel recognises it

When you paste code, Unravel looks for these signs of concurrent collections. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Builds on this

Frequently asked questions

What is Concurrent collections?

Concurrent collections are safe to share between threads. ConcurrentHashMap allows many readers and writers, and a BlockingQueue lets producer and consumer threads hand work to each other.

What are the most common mistakes with Concurrent collections?

Check-then-act with separate calls. Expecting null values in ConcurrentHashMap. Unbounded queues. CopyOnWriteArrayList for frequently modified data.

How can I see Concurrent collections in real code?

Paste your own Java into Unravel and it will point out where concurrent collections appears, explain how it works at the detail level you choose, and draw the flow of the code around it.