Concurrency in Java
Concurrency in Java is running several threads inside one process. All threads share the process heap and loaded classes, but each has its own stack. That makes exchanging data fast — nothing needs to be copied — but dangerous: two threads can read and write the same object at once. The scheduler does not guarantee the order of their steps, so correctness must never rest on "the timing worked out".
The foundation is the Java Memory Model (JMM) and the happens-before relationship. Without explicit synchronization, one thread has no guarantee of seeing another's write: the value may sit in a core cache or a register. The monitor (synchronized), volatile, and the classes in java.util.concurrent establish happens-before — a boundary after which changes are guaranteed visible. Keep two distinct guarantees apart: visibility (will a thread see the fresh value) and atomicity (will a compound operation not tear midway). Conflating them causes most of the bugs below. The full map lives in the layers.
Topic map
- Process vs Thread — a process is isolated by its own memory; a thread lives inside a process, shares its heap, but keeps its own stack.
- Thread Creation — extending
Threadvs implementingRunnable, theExecutorServicepool, and a result viaFuture. - Synchronization —
synchronizedacquires an object's monitor and serializes the critical section, establishing happens-before. - The volatile Keyword — guarantees visibility and no reordering, but NOT atomicity of compound operations.
- wait vs sleep —
wait()releases the monitor and waits fornotify();sleep()keeps every lock. - Concurrency Hazards — race condition, deadlock, livelock, and starvation: how they differ and what fixes each.
- Effectively-Final Capture — lambdas and anonymous classes capture only effectively-final local variables.
Common traps
| Mistake | Consequence |
|---|---|
Calling run() instead of start() | The code runs on the current thread — no new thread appears |
Treating volatile as atomic for count++ | The read-modify-write interleaves and updates are lost |
Calling wait() outside synchronized | An IllegalMonitorStateException at runtime |
Thinking sleep() releases the monitor | The sleeping thread holds the lock — others stall |
Capturing a for loop variable in a lambda | Does not compile: the variable is not effectively final |
| Taking two locks in different orders | A circular wait → deadlock |
notify() with mixed wait conditions | The wrong thread wakes and a runnable one waits forever |
Interview relevance
Concurrency is one of the main filters on middle/senior Java interviews. The check is your model of shared state, not keyword recall: do you understand that a monitor gives both mutual exclusion and visibility while volatile gives visibility only; that a race condition is a correctness problem, not a speed one; that deadlock is fixed by a consistent lock order, not by priorities.
Typical checks:
- The difference between a process and a thread, and exactly what threads share (the heap) versus what is private (the stack).
- The ways to create a thread and why
Runnableis preferred over extendingThread. - What
synchronizeddoes — serialize the section on an object's monitor and establish happens-before. - That
volatileguarantees visibility but not atomicity ofi++. - The difference between
wait()andsleep()regarding the held monitor. - Race, deadlock, and livelock — how to tell them apart and how each is fixed.
Common wrong answer: "volatile makes a counter thread-safe." That opens the discussion that count++ is a three-step read-modify-write two threads interleave, losing updates; volatile only synchronizes the visibility of a single read and write, while an atomic increment comes from synchronized or AtomicInteger.