java.util.concurrent
Explicit locks and reentrancy, Condition objects, CAS and atomics, synchronizers, and concurrent collections including ConcurrentHashMap.
9 questions
MiddleTheoryVery commonWhat is compare-and-swap CAS, and how does AtomicInteger use it to avoid locks?
What is compare-and-swap CAS, and how does AtomicInteger use it to avoid locks?
CAS is a single atomic hardware instruction: it writes a new value only if the memory still holds the expected old one, and fails otherwise. AtomicInteger.incrementAndGet() reads the value, computes old + 1, and retries the CAS in a loop until it wins. No thread ever blocks — a loser just re-reads and tries again, so there is no lock and no context switch.
Common mistakes
- ✗Thinking
volatilealone makescount++atomic - ✗Believing a failed
CASblocks the caller instead of reporting failure so the loop can retry - ✗Assuming atomics always beat a lock, even under contention heavy enough to pile up retries
Follow-up questions
- →Why can
AtomicIntegerlose to a plain lock under very high contention? - →Which
Atomicclass suits a counter written far more often than it is read?
MiddleTheoryVery commonWhat does ReentrantLock offer that synchronized cannot?
What does ReentrantLock offer that synchronized cannot?
Both are reentrant mutual-exclusion locks, but ReentrantLock is an explicit object, so it can add what a block cannot: tryLock() with a timeout, an interruptible acquire, an optional fair queue, and several Condition objects. The price is manual discipline — you must call unlock() in a finally block, whereas synchronized releases automatically when the block exits.
Common mistakes
- ✗Forgetting
unlock()in afinallyblock, so a thrown exception leaks the lock forever - ✗Believing
synchronizedsupports a timed or interruptible acquire - ✗Assuming
ReentrantLockis fair by default — it barges unless constructed withtrue
Follow-up questions
- →What happens if an exception skips the
unlock()call on a heldReentrantLock? - →What does a fair
ReentrantLockcost compared with the default barging one?
JuniorTheoryCommonWhat is lock reentrancy, and why do synchronized and ReentrantLock support it?
What is lock reentrancy, and why do synchronized and ReentrantLock support it?
Reentrancy means a thread already holding a lock can acquire it again instead of deadlocking on itself. The lock tracks the owning thread plus a hold count: each re-acquire increments it and each release decrements it, freeing the lock only when the count returns to zero. Both synchronized and ReentrantLock are reentrant, so one synchronized method can safely call another on the same object.
Common mistakes
- ✗Believing a thread deadlocks itself when it re-enters a lock it already holds
- ✗Forgetting the lock frees only after every acquire is matched by a release
- ✗Thinking
ReentrantLockis reentrant but intrinsicsynchronizedmonitors are not
Follow-up questions
- →What happens if a thread unlocks a
ReentrantLockfewer times than it locked it? - →Why would a lock that is not reentrant deadlock a recursive synchronized call?
JuniorTheoryCommonHow do Collections.synchronizedList() and CopyOnWriteArrayList differ?
How do Collections.synchronizedList() and CopyOnWriteArrayList differ?
Collections.synchronizedList() wraps a list so that one lock guards every method: reads block each other, and iteration must still be synchronized by hand. CopyOnWriteArrayList copies the backing array on each write, so reads take no lock at all and every iterator sees an immutable snapshot.
Common mistakes
- ✗Thinking a synchronized wrapper makes iteration safe without an external lock around the loop
- ✗Choosing
CopyOnWriteArrayListfor write-heavy data, where every write copies the whole array - ✗Expecting a
CopyOnWriteArrayListiterator to see writes made after it was created
Follow-up questions
- →Why does iterating a
Collections.synchronizedList()still need an external lock? - →At what write rate does
CopyOnWriteArrayListstop being a sensible choice?
MiddleTheoryCommonHow does ConcurrentHashMap avoid locking the whole map on a write?
How does ConcurrentHashMap avoid locking the whole map on a write?
Reads take no lock: the table and its nodes are volatile, so get() simply walks the bin. A write locks only the head node of the one bin it hashes to, so writers landing in different bins proceed in parallel, and an empty bin is filled with a compare-and-swap instead. Java 8 replaced Java 7's fixed segment array with this per-bin locking.
Common mistakes
- ✗Thinking Java 8
ConcurrentHashMapstill locks a fixed segment rather than a single bin - ✗Believing
get()takes a lock or blocks against a concurrent writer - ✗Assuming a check-then-put pair is atomic without
computeIfAbsentorputIfAbsent
Follow-up questions
- →Why is
computeIfAbsentatomic where aget()followed by aput()is not? - →What does
ConcurrentHashMapdo when one bin grows long enough to become a tree?
MiddleTheoryCommonHow do Condition objects improve on wait/notify?
How do Condition objects improve on wait/notify?
An intrinsic monitor gives an object exactly one wait set, so producers and consumers queue together and notifyAll() has to wake everyone just to reach the right thread. A ReentrantLock can create several Condition objects, each its own wait set, so notFull.signal() wakes only producers. await()/signal() otherwise mirror wait()/notify() and demand the lock be held.
Common mistakes
- ✗Calling
await()orsignal()without holding the lock, which throwsIllegalMonitorStateException - ✗Waiting with an
ifinstead of awhileloop that re-checks the predicate after waking - ✗Believing a single object can already own several intrinsic wait sets
Follow-up questions
- →Why must
await()always sit inside awhileloop rather than anif? - →When is
signalAll()still required even with separateConditionobjects?
MiddleTheoryCommonHow does CountDownLatch differ from CyclicBarrier?
How does CountDownLatch differ from CyclicBarrier?
A CountDownLatch is one-shot and one-directional: waiters block in await() until other threads drive the count to zero with countDown(), and it can never be reset. A CyclicBarrier is a rendezvous of N parties that wait for each other; when the last one arrives all are released, an optional barrier action runs, and the barrier resets for the next round.
Common mistakes
- ✗Trying to reuse a
CountDownLatchafter its count has already reached zero - ✗Thinking
countDown()blocks its caller, when onlyawait()ever blocks - ✗Forgetting a
CyclicBarrierbreaks for every party when one is interrupted or times out
Follow-up questions
- →What happens to the other parties when one thread waiting at a
CyclicBarrieris interrupted? - →Which synchronizer fits a start signal that releases many threads at once?
JuniorTheoryOccasionalWhat does the java.util.concurrent package add over synchronized and wait/notify?
What does the java.util.concurrent package add over synchronized and wait/notify?
It replaces hand-rolled monitor code with tested building blocks: explicit locks, atomics, concurrent collections, executor pools and synchronizers. You compose ready primitives instead of writing wait/notify loops yourself.
Common mistakes
- ✗Assuming
java.util.concurrentis only sugar oversynchronizedwith no new mechanism - ✗Reaching for raw
wait/notifywhen a ready-made synchronizer or executor fits - ✗Thinking the package removes real OS threads rather than building on them
Follow-up questions
- →Which
java.util.concurrentclass would you use to cap the number of concurrent tasks? - →Why is an
ExecutorServiceusually preferable to creating rawThreadobjects?
SeniorTheoryOccasionalWhat is the ABA problem in a compare-and-swap CAS loop, and how is it mitigated?
What is the ABA problem in a compare-and-swap CAS loop, and how is it mitigated?
A thread reads value A, stalls while another thread changes it to B and back to A, then its CAS succeeds even though the state it reasoned about is gone — in a lock-free stack a popped node can be recycled and re-linked, corrupting the list. The fix is to version the reference: AtomicStampedReference pairs it with a counter, so the CAS compares both and fails after any intervening change.
Common mistakes
- ✗Thinking a successful
CASproves nothing changed in between - ✗Believing
volatileor a longer retry loop preventsABA - ✗Assuming
ABAis harmless because the observed value ends up identical
Follow-up questions
- →Why is
ABAmostly harmless for a counter yet dangerous for a lock-free stack? - →How does
AtomicMarkableReferencediffer fromAtomicStampedReference?