skip to content

Synchronization

Mutual exclusion through Java's intrinsic locks and the synchronized keyword, including the memory-visibility effects that come with them. Interviewers use it to check you understand that synchronization is about visibility as much as exclusion.

part ofJavaoverview, primer and where to startread it →
on this pageshow

questions

15

What is lock contention in a multithreaded Java program, and why does it hurt performance?

level: juniorimportance: must knowfreq 72%

answer

  1. One holder, many waiters
  2. Wait time + context-switch overhead
  3. Serialization → Amdahl's law caps speedup
  4. Contended counter can be slower than single-threaded
  5. Fix: shorter critical section, finer locks, or no lock

basics

~20 s

Lock contention happens when several threads try to acquire the same lock at the same time. Only one can hold it, so the others wait. The more threads compete, the more time is spent waiting instead of doing real work, so the program slows down.

solid answer

~40 s

Lock contention is when multiple threads compete for the same lock simultaneously. A lock is mutually exclusive, so only one thread holds it at a time; the rest block until it is released. Contention hurts performance two ways: directly, through the time threads spend waiting (and the OS context-switch / thread-park overhead to put waiters to sleep and wake them), and indirectly, by serializing work that could otherwise run in parallel, capping your speedup (Amdahl's law). Under heavy contention a synchronized section can even run slower than single-threaded code because of the coordination overhead. The fixes are to hold locks for less time (narrow the critical section), lock at a finer granularity so threads contend on different locks, or avoid locking via immutable data, thread-confinement, or lock-free atomics.

go deeper

for a junior

Can define lock contention as threads waiting for the same lock and say it slows the program because only one thread proceeds at a time.

for a middle

Adds the mechanism — blocking, parking, context-switch overhead — and knows uncontended locks are cheap while contended ones aren't.

for a senior

Frames contention as serialization governed by Amdahl's law, can reason about when it dominates, and names concrete reduction strategies (narrower sections, finer locks, lock-free).

for a principal

Reasons about system-level impact (scalability ceilings, tail latency, throughput collapse under load), measurement via JFR/profilers, and chooses between lock-splitting, lock-free, and architectural changes (sharding, immutability) with trade-offs.

## First principles When two or more threads share mutable data, you need **mutual exclusion** so they don't corrupt it by interleaving reads and writes. In Java the basic tool is a **lock** (the `synchronized` keyword, or a `java.util.concurrent.locks.Lock` such as `ReentrantLock`). A lock has a simple rule: **at most one thread can hold it at a time.** The region of code that runs while holding the lock is the **critical section**. ## What 'contention' means **Contention** = competition. **Lock contention** is the situation where, at the moment a thread wants to *acquire* a lock, another thread already *holds* it (or several threads are queued for it). The wanting threads cannot proceed; they **block** (are suspended) until the lock is free. - **Uncontended lock:** when a thread asks for the lock, it's free, so it takes it immediately and moves on. This is cheap — on the JVM an uncontended `synchronized` is often optimized to a handful of instructions (biased/thin locking, lock elision). - **Contended lock:** the thread must wait. The JVM may first **spin** (busy-loop briefly, hoping the holder releases soon), and if that fails, **park** the thread — hand it to the OS, which removes it from the CPU and schedules another thread. Waking it later is a **context switch**. ## Why it hurts performance 1. **Waiting time.** A blocked thread does no useful work while it waits. 2. **Coordination overhead.** Parking and unparking threads costs CPU and triggers OS context switches, cache disturbance, and scheduler work. Under heavy contention this overhead can dominate. 3. **Serialization (the deep reason).** A lock forces the protected work to run **one thread at a time**. So even with 16 cores, the locked portion runs sequentially. **Amdahl's law** says your maximum speedup is limited by the fraction of work that must be serial: if 20% of the work is inside one global lock, you can never go faster than 5x no matter how many cores you add. Contention is how that serial fraction shows up at runtime. A classic, counter-intuitive result: a heavily contended `synchronized` counter can be *slower* with many threads than with one, because the threads spend almost all their time fighting over the lock and being context-switched, not incrementing. ## How you reduce it (preview) - **Narrow the critical section:** do less work while holding the lock — move I/O, allocation, and pure computation outside it. - **Finer granularity:** split one big lock into several, so threads that touch *different* data contend on *different* locks (e.g. lock striping in `ConcurrentHashMap`). - **Avoid locking entirely:** immutable objects (no writes to coordinate), thread-confined data (each thread owns its own copy), read-mostly data behind `ReadWriteLock`/`StampedLock`, or lock-free atomics (`AtomicLong`, `LongAdder`). ## How you *see* it Thread dumps showing many threads `BLOCKED` on the same monitor, profilers/JFR 'monitor blocked' or 'lock contention' events, and `LongAdder` outperforming `AtomicLong` under load are all signals of contention.

  • Why can a heavily contended synchronized counter be slower than a single-threaded one?
    Because the threads spend most of their time acquiring/releasing the lock and being parked/unparked (context switches) rather than incrementing; the actual increment work is serialized anyway, so you pay coordination overhead on top of sequential execution.
  • Is an uncontended lock expensive?
    No. The JVM optimizes uncontended locking heavily (biased/thin locks, and even lock elision via escape analysis), so it costs very little. The cost appears mainly when threads actually compete.

saying these in an interview costs you the question

  • Claiming any synchronized code is automatically slow — uncontended locks are cheap
  • Confusing contention (waiting for a lock) with deadlock (mutual permanent wait)
  • Thinking more threads always means more throughput regardless of locking
  • Saying contention causes incorrect results — it causes slowness, not data corruption

context

open as a page

What is the difference between a synchronized method and a synchronized block in Java, and when would you prefer one over the other?

level: juniorimportance: must knowfreq 78%

basics

~20 s

Both let only one thread run the protected code at a time. A synchronized method locks the whole method on the instance (or the class for static methods). A synchronized block locks only a chosen region and a lock object you pick, so it can be narrower.

open as a page

A boolean flag is updated by a writer inside a synchronized block but read by a worker loop without synchronization. Why might the worker never see the update, and how do the synchronized memory effects explain the fix?

level: juniorimportance: must knowfreq 64%

basics

~20 s

The reader can keep using a stale cached copy of the flag because nothing forces it to refresh from main memory. The fix is to read the flag with the same synchronization (same lock, or make it volatile) so entering re-reads the latest value the writer flushed.

open as a page

How does narrowing a critical section reduce contention, and what work should you move out of a lock?

level: middleimportance: must knowfreq 64%

basics

~20 s

Hold the lock for as short a time as possible. Only the steps that actually touch shared data need the lock. Move slow or unrelated work — I/O, network calls, logging, big computations, object creation — outside the locked block so other threads wait less.

open as a page

What is an intrinsic lock (monitor) in Java, and how does synchronized use it?

level: middleimportance: must knowfreq 70%

basics

~20 s

Every Java object has one built-in lock called its intrinsic lock or monitor. The synchronized keyword acquires that lock when a thread enters the region and releases it when the thread leaves, so only one thread at a time can hold it.

open as a page

How does the object-level lock used by an instance synchronized method differ from the class-level lock used by a static synchronized method, and why does it matter?

level: middleimportance: must knowfreq 64%

basics

~20 s

An instance synchronized method locks on that one object (this), so each instance has its own lock. A static synchronized method locks on the Class object, shared by all instances. The two locks are independent: a thread in a static synchronized method does not block a thread in an instance synchronized method.

open as a page

Beyond mutual exclusion, what memory effects does Java's synchronized keyword guarantee when a thread exits and another thread later enters a synchronized block on the same lock?

level: middleimportance: must knowfreq 72%

basics

~20 s

synchronized does two jobs. It blocks other threads from the same locked code (mutual exclusion), and it makes memory visible: when a thread leaves the block its changes are pushed to main memory, and when another thread enters on the same lock it re-reads fresh values instead of stale cached ones.

open as a page

Compare coarse-grained and fine-grained locking. What are the trade-offs, and how does lock striping help?

level: seniorimportance: must knowfreq 68%

basics

~20 s

A coarse-grained lock protects everything with one lock: simple, but all threads queue on it. Fine-grained locking uses many locks for different pieces of data, so unrelated threads don't block each other — but it's more complex and can cause deadlock. Lock striping splits a structure into segments, each with its own lock.

open as a page

Why is choosing the lock object carefully important, and what are the consequences of synchronizing on a shared or publicly accessible object versus a private one?

level: seniorimportance: must knowfreq 58%

basics

~20 s

The lock object determines what is mutually exclusive. If you lock on something other code can also see and lock (like this or a String/Integer), outside code can interfere, cause unexpected contention, or deadlock. Locking on a private final object you fully control keeps the lock policy safe and encapsulated.

open as a page

What does it mean that Java's intrinsic locks are reentrant, and what problem does reentrancy prevent?

level: middleimportance: should knowfreq 55%

basics

~20 s

Reentrant means a thread that already holds a lock can acquire the same lock again without blocking itself. This lets a synchronized method safely call another synchronized method on the same object. Without it, a thread could deadlock against a lock it already owns.

open as a page

How do you detect and quantify lock contention in a running Java application?

level: seniorimportance: should knowfreq 52%

basics

~20 s

Look for threads stuck waiting on the same lock. A thread dump shows BLOCKED threads and what monitor they're waiting for. Tools like Java Flight Recorder and async-profiler record lock-contention events, telling you which locks are hot and how long threads wait.

open as a page

Why does even an empty synchronized block carry memory-barrier (acquire/release) effects, and what does this reveal about where Java's visibility guarantee comes from?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Because the visibility comes from taking and releasing the lock, not from the code inside. Entering the block forces a fresh read of shared memory and exiting forces your changes out to memory, so an empty synchronized block still publishes and refreshes data.

open as a page

How do the memory effects of a synchronized block compare to those of a volatile field, and when does each carry an additional guarantee the other does not?

level: seniorimportance: should knowfreq 55%

basics

~20 s

Both create the same acquire/release visibility: entering/reading refreshes from memory, exiting/writing flushes to memory. synchronized adds mutual exclusion, so it can make a multi-step operation atomic. volatile only gives visibility for a single field, with no locking and no atomicity for compound actions.

open as a page

When should you replace a contended lock with a lock-free or contention-avoiding alternative, and what are the options?

level: principalimportance: should knowfreq 48%

basics

~20 s

When a lock is a proven hotspot, you can avoid it instead of shrinking it. Options: immutable objects (nothing to lock), thread-confined data (each thread its own copy), read-mostly locks, atomic CAS classes like AtomicLong, or LongAdder for hot counters. They reduce or remove contention.

open as a page

Design-wise, why is guarding a shared field's writes and reads with two different monitors a correctness bug even though each access is technically 'synchronized', and how would you reason about and prevent this class of error at scale?

level: principalimportance: should knowfreq 30%

basics

~20 s

The happens-before edge only forms between unlock and lock of the same monitor. If writes use lock A and reads use lock B, there's no edge between them, so a reader can see stale or reordered data even though both sides 'use a lock'. Prevent it by binding each piece of shared state to one documented lock.

open as a page