The Java Memory Model (JMM) Interview Questions
KOTLIN › Concurrency
Walk me through why 'thread-safe' isn't a single yes/no property. Give an example, like ConcurrentHashMap, where an individual operation is thread-safe but a common two-step usage built on top of it is not.
What a strong answer covers: Each individual method on ConcurrentHashMap, get, put, containsKey, is internally atomic and safe to call from any thread, but a sequence like 'if (!map.containsKey(k)) map.put(k, v)' is not atomic as a whole, another thread can insert between the check and the act, producing a lost update or a duplicate. The fix is to use the compound atomic methods the class provides for exactly this, putIfAbsent or computeIfAbsent, which perform the check-then-act inside the class's own internal locking rather than composing two separate atomic calls at the app level.
A data class with var fields is constructed on one thread and its reference is published to another thread with no lock and no volatile. Is it safe for the second thread to read those fields? Walk me through your reasoning.
What a strong answer covers: No; the JMM's safe-publication guarantee for constructor writes only applies to fields that are actually final, Kotlin val, and that don't escape during construction, so with var fields the compiler and CPU are free to reorder the constructor's writes relative to the publication of the reference itself. That means the second thread can legally observe default/zero values or a partially-constructed object even though it holds a non-null reference; the fix is making the fields val, or adding real synchronization, a volatile reference or a lock, around the publication.
A teammate wraps a slow network call in a synchronized block to 'make it thread-safe.' Why is this actually a worse bug than the race it was meant to fix?
What a strong answer covers: synchronized isn't just a happens-before edge, it's mutual exclusion, so any other thread trying to enter that same monitor now blocks for the entire duration of the slow call, turning a possible data race into a guaranteed contention problem, and on the UI thread that's an ANR waiting to happen. The correct fix is to synchronize only around the actual shared-state access, not the I/O itself, and to reach for non-blocking coordination or a coroutine Mutex when the protected section needs to contain suspending work.