ReviseAlgo Logo

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.

Last Updated: June 13, 2026 10 min read

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.