Common Java Pitfalls
Concurrency Mistakes
Learn about race conditions, deadlocks, and the dangers of double-checked locking without volatile.
Interview: Commonly tested for senior and backend positions. Focuses on deadlock structures, volatile memory visibility, and double-checked locking safety.
Concurrency bugs are notoriously difficult to debug because they are non-deterministic, depending on CPU scheduling, hardware caches, and memory instruction reorderings.
Race Condition
Occurs when multiple threads read and write shared state simultaneously without proper coordination, leading to corrupted data.
Deadlock
A state where two or more threads are permanently blocked, waiting for locks held by each other.
Instruction Reordering
JVM or CPU optimizes execution by reordering operations, which can expose half-constructed states to other threads.
The Double-Checked Locking Trap
In Singleton designs, developers try to optimize synchronization using double-checked locking. If the target instance variable is not marked as volatile, this pattern is completely broken.
Why? The operation instance = new Singleton(); is not atomic. It compiles to: (1) allocate memory, (2) run constructor, (3) write address to variable. The JVM/CPU can reorder this to (1) -> (3) -> (2). If Thread A writes the address (3) before running the constructor (2), Thread B reads a non-null but uninitialized instance, crashing when accessing object properties.
Interview-Relevant Information
Q: How does the volatile keyword solve the reordering hazard?
Answer: volatile enforces two guarantees: (1) Visibility: Writes to the variable are immediately written to main memory and made visible to all threads. (2) Happens-Before Ordering: It creates a memory barrier, preventing instructions before the write from being reordered after it.
Q: What are the four conditions for a Deadlock?
Answer: All four must hold simultaneously: (1) Mutual Exclusion: At least one resource is held in a non-shareable mode. (2) Hold and Wait: A thread holds a resource while waiting for another. (3) No Preemption: Resources cannot be forcibly taken. (4) Circular Wait: A closed loop of threads exists where each waits for a resource held by the next. Breaking *any* of these conditions prevents deadlocks.
Quick Checklist
Can you write a thread-safe Singleton using volatile? Do you know how to break deadlocks by acquiring locks in a strict global order? If yes, you understand common concurrency bugs.
Use Cases
Preventing inconsistent state mutations in shared server configuration beans.
Safely constructing lazy-initialized singleton application services.
Common Mistakes
Assuming thread safety by wrapping a non-thread-safe collection (like HashMap) in a volatile reference (volatile only protects reference swaps, not internal content).
Calling thread blocking methods like wait() or sleep() inside synchronized blocks while holding lock locks, blocking execution systems.