skip to content

Lock Contention & Granularity

Contention is what happens when threads queue for the same lock, and granularity is the dial between simple-but-serializing coarse locks and concurrent-but-deadlock-prone fine ones. Interviewers ask how you would shrink a critical section without breaking correctness.

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

questions

5

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

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

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

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

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