Locks, Atomics & Coordination Quiz

KOTLIN › JVM Concurrency

Which approach removes the race rather than managing it?

Answer: Immutability, or confining all access to a single thread

If nothing can write, or only one thread ever writes, there is no race to manage. Volatile fixes visibility but not atomicity, and a concurrent map is itself a synchronisation primitive.

Two instances of a class each call the same @Synchronized instance method concurrently. What happens?

Answer: Both proceed, since each locks on its own instance

An instance method locks on this, so two objects mean two monitors. Only a static synchronized method locks the shared Class object.

What does ReentrantLock provide that synchronized does not?

Answer: tryLock with a timeout, interruptible acquisition, fairness, and Condition wait sets

Both are reentrant and both publish memory. The extras are all about controlling the wait, and the cost is that you must release it yourself.

You need at most five uploads in flight from a hundred queued. Which tool is most accurate?

Answer: A coroutine Semaphore(5) with withPermit

limitedParallelism bounds concurrent execution, and an upload suspended on the network is not executing, so more than five could be outstanding. A semaphore bounds how many are inside the section. A Mutex would serialise to one.

What is the difference between a CountDownLatch and a CyclicBarrier?

Answer: A latch is one-shot and counts events; a barrier is reusable and counts arriving threads

A latch counts to zero once and stays there, so waiters and counters may differ. A barrier needs every participant to arrive and wait, then resets, which suits phased computation.

Why can AtomicLong perform worse than a lock under heavy write contention?

Answer: The compare-and-set retry loop burns CPU as threads collide repeatedly

CAS is optimistic and retries on collision, so contention becomes wasted spinning. LongAdder gives each thread its own cell and sums on read, trading an exact instantaneous value for throughput.

What does the ABA problem demonstrate?

Answer: CAS checks the current value, not that the value never changed in between

A value that goes A to B and back to A passes the comparison even though the state moved on. AtomicStampedReference adds a version so the stale comparison fails.

Which deadlock condition does consistent lock ordering eliminate?

Answer: Circular wait

A cycle requires someone acquiring in the opposite order, which a global ordering forbids. tryLock with a timeout is the alternative and attacks hold and wait instead.

What distinguishes livelock from deadlock?

Answer: Livelocked threads are actively running and reacting to each other, making no progress

Deadlocked threads are blocked and idle in a thread dump; livelocked threads look healthy and burn CPU retrying in lockstep. Randomised backoff breaks the symmetry.

Why must a critical section that suspends use a coroutine Mutex rather than synchronized?

Answer: A monitor is thread-owned, and a coroutine may resume on a different thread

Releasing a monitor from a thread that never acquired it is illegal, and a coroutine resuming elsewhere would do exactly that. Mutex is coroutine-aware and suspends instead of blocking. Note the reentrancy is the other way round: monitors are reentrant, Mutex is not.

What happens if you try to upgrade a held read lock to a write lock on a ReentrantReadWriteLock?

Answer: It deadlocks, because the write lock waits for all readers including yourself

The write lock is exclusive against every reader, and you are one of them, so you wait on yourself forever. Downgrading is legal: acquire write, then read, then release write.

What is priority inversion on Android?

Answer: A low-priority background thread holds a lock the main thread needs, and the scheduler throttles it

A background-priority thread sits in a cgroup with a small CPU share, so it is slow to release the lock and the UI stalls on work the scheduler is deliberately starving. It argues against sharing locks between main and low-priority work.

Which combination can hold a ReentrantReadWriteLock at the same moment?

Answer: Several threads holding the read lock

Readers share: any number may hold the read lock concurrently. The write lock is exclusive against both readers and writers. That's the whole value: read-heavy structures stop serialising their readers.

A thread holding the READ lock of a ReentrantReadWriteLock tries to acquire the WRITE lock. What happens?

Answer: It deadlocks: upgrading is not supported

The write lock must wait for all readers to release, including the requesting thread itself, which is blocked waiting: a self-deadlock. Downgrading (acquire read while holding write, then release write) IS allowed. To "upgrade", release the read lock first and re-check state after acquiring write.

Why is a coroutine Mutex preferred over synchronized around code that calls suspend functions?

Answer: A coroutine can resume on a different thread, breaking thread-owned monitors; Mutex suspends and is coroutine-owned

Monitors are held by threads. Suspend inside the block and the coroutine may resume elsewhere while the original thread still owns (or has corrupted) the monitor. mutex.withLock suspends waiters without blocking threads and releases on the resuming context correctly.

In double-checked locking for a lazy singleton, why must the INSTANCE field be @Volatile?

Answer: So a half-constructed object can't be published to other threads

Without volatile, the reference write can be reordered before the constructor finishes, so another thread's fast-path null check sees a non-null but uninitialised object. Volatile's ordering forbids that publication reorder. (Kotlin's lazy { } or an object declaration handles this for you.)

Which tool is best for protecting a short suspend-capable read-modify-write on a shared MutableMap inside coroutines?

Answer: Mutex.withLock { ... } because it suspends instead of blocking and allows suspension inside the critical section

Mutex.withLock suspends rather than blocking and allows suspend calls inside the critical section. synchronized blocks the thread (bad for coroutines), AtomicReference can't protect multi-step operations on a map, and Default is a pool not a single-thread confiner.

Back to Locks, Atomics & Coordination