[{"data":1,"prerenderedAt":151},["ShallowReactive",2],{"qa-\u002Fjava\u002Fconcurrency\u002Fsynchronization-locks":3},{"page":4,"siblings":130,"blog":148},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":120,"related":121,"seo":122,"seoDescription":123,"stem":124,"subtopic":125,"topic":126,"topicSlug":127,"updated":128,"__hash__":129},"qa\u002Fjava\u002Fconcurrency\u002Fsynchronization-locks.md","Synchronization Locks",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","Java","java",{},true,"\u002Fjava\u002Fconcurrency\u002Fsynchronization-locks",[22,27,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116],{"id":23,"difficulty":24,"q":25,"a":26},"what-is-synchronization","medium","What is synchronization and what problem does it solve?","**Synchronization** restricts a block of code so that only **one thread at a\ntime** can execute it (mutual exclusion) and ensures changes made by one thread\nare **visible** to others. It exists to fix the **race condition**: when two\nthreads access shared mutable state concurrently and at least one writes, the\nfinal result depends on unpredictable timing.\n\n```java\nint count = 0;\nvoid inc() { count++; }   \u002F\u002F NOT atomic: read, add, write\n```\n\n`count++` is three steps; two threads can both read the same value and one\nupdate is lost. The vulnerable region — the code touching shared state — is the\n**critical section**, and synchronization serializes access to it.\n",{"id":28,"difficulty":29,"q":30,"a":31},"critical-section","easy","What is a critical section?","A **critical section** is a region of code that accesses **shared mutable\nstate** and must not be executed by more than one thread simultaneously.\nProtecting it with a lock guarantees that interleaved access can't corrupt the\ndata.\n\n```java\nsynchronized (lock) {\n  \u002F\u002F critical section: only one thread in here at a time\n  balance += amount;\n}\n```\n\nThe goal is to make the critical section **as small as correctness allows** —\nhold the lock just long enough to read\u002Fmodify the shared data, no longer, so\nother threads aren't blocked unnecessarily.\n",{"id":33,"difficulty":24,"q":34,"a":35},"synchronized-keyword","How does the synchronized keyword work?","`synchronized` acquires an object's **intrinsic lock** (also called its\n**monitor**) before entering, and releases it on exit — even if an exception is\nthrown. While one thread holds the lock, any other thread trying to synchronize\non the **same object** blocks.\n\n```java\nsynchronized void m() { ... }          \u002F\u002F locks on 'this'\nsynchronized (lockObj) { ... }         \u002F\u002F locks on lockObj\nstatic synchronized void s() { ... }   \u002F\u002F locks on the Class object\n```\n\nIt can be applied to a **method** (locks the receiver, or the `Class` for static\nmethods) or a **block** (you choose the lock object). Two threads synchronizing\non *different* objects don't block each other at all.\n",{"id":37,"difficulty":24,"q":38,"a":39},"synchronized-method-vs-block","What is the difference between a synchronized method and a synchronized block?","A **synchronized method** implicitly locks the **whole method body** on `this`\n(or the `Class` for static methods). A **synchronized block** lets you lock only\na **portion** of the method, and on **any object you choose**.\n\n```java\nsynchronized void update() {        \u002F\u002F entire method under lock\n  prepare();                        \u002F\u002F doesn't need the lock — wasteful\n  shared.modify();\n}\n\nvoid update2() {\n  prepare();                        \u002F\u002F runs concurrently\n  synchronized (lock) {             \u002F\u002F only the critical part is locked\n    shared.modify();\n  }\n}\n```\n\nBlocks give **finer granularity** (shorter lock hold time, higher concurrency)\nand let you avoid locking on `this`, which is the safer practice.\n",{"id":41,"difficulty":24,"q":42,"a":43},"intrinsic-lock-object","What object does a synchronized method lock on?","It depends on whether the method is static:\n\n| Method kind | Lock acquired |\n| ----------- | ------------- |\n| instance `synchronized` method | the **instance** (`this`) |\n| static `synchronized` method | the **`Class` object** (e.g. `Foo.class`) |\n| `synchronized (obj)` block | whatever `obj` you name |\n\n```java\nclass Foo {\n  synchronized void a() {}        \u002F\u002F locks 'this'\n  static synchronized void b() {} \u002F\u002F locks Foo.class\n}\n```\n\nCrucially, the instance lock and the `Class` lock are **different monitors** —\nso `a()` and `b()` can run **concurrently** on the same object. Mixing static\nand instance synchronization without realizing this is a common bug.\n",{"id":45,"difficulty":24,"q":46,"a":47},"reentrancy","What does it mean that intrinsic locks are reentrant?","**Reentrant** means a thread that already holds a lock can acquire it **again**\nwithout deadlocking itself. The JVM tracks an owner and a hold count;\nre-entering increments it, exiting decrements it, and the lock frees only when\nthe count hits zero.\n\n```java\nsynchronized void outer() {\n  inner();          \u002F\u002F re-acquires the same lock — fine\n}\nsynchronized void inner() { ... }\n```\n\nWithout reentrancy, `outer()` calling `inner()` (both locking `this`) would\nblock forever. This is why a synchronized method can freely call other\nsynchronized methods on the same object. `ReentrantLock` provides the same\nbehavior explicitly.\n",{"id":49,"difficulty":14,"q":50,"a":51},"happens-before","What memory visibility guarantee does a lock provide?","A lock establishes a **happens-before** relationship: everything a thread does\n**before releasing** a lock is **visible** to any thread that **subsequently\nacquires** the same lock. So locks aren't just about mutual exclusion — they\nalso flush writes to main memory and invalidate stale cached reads.\n\n```java\nsynchronized (lock) { data = 42; }   \u002F\u002F write happens-before release\n\u002F\u002F another thread:\nsynchronized (lock) { read(data); }  \u002F\u002F sees 42 — acquire sees prior release\n```\n\nWithout synchronization (or `volatile`), one thread's write to a field may\n**never** become visible to another due to CPU caching and reordering. This\nvisibility guarantee is why you can't fix a race with `synchronized` on writes\nbut unsynchronized reads — both sides must use the lock.\n",{"id":53,"difficulty":14,"q":54,"a":55},"wait-notify","How do wait(), notify() and notifyAll() work?","They coordinate threads on an object's **monitor**. `wait()` **releases the\nlock** and parks the thread until another thread calls `notify()`\u002F`notifyAll()`\non the **same object**; the woken thread must **re-acquire** the lock before\ncontinuing. `notify()` wakes **one** waiting thread, `notifyAll()` wakes **all**.\n\n```java\nsynchronized (queue) {\n  while (queue.isEmpty())   \u002F\u002F guard in a loop, not an if\n    queue.wait();           \u002F\u002F releases lock, sleeps until notified\n  process(queue.remove());\n}\n\u002F\u002F producer:\nsynchronized (queue) { queue.add(x); queue.notifyAll(); }\n```\n\nYou **must hold the monitor** to call any of them, or you get\n`IllegalMonitorStateException`. Prefer `notifyAll()` unless you can prove a\nsingle waiter is always the right one.\n",{"id":57,"difficulty":14,"q":58,"a":59},"wait-while-loop","Why must wait() be called inside a while loop?","Because a thread can return from `wait()` for reasons other than the condition\nbecoming true: **spurious wakeups** (the JVM is allowed to wake a thread for no\nreason) and the fact that with `notifyAll()` **multiple** threads wake and race,\nso by the time one re-acquires the lock the condition may be false again.\n\n```java\n\u002F\u002F WRONG — assumes the condition holds after waking\nif (queue.isEmpty()) queue.wait();\n\n\u002F\u002F RIGHT — re-checks after every wakeup\nwhile (queue.isEmpty()) queue.wait();\n```\n\nThe `while` loop **re-tests the predicate** after re-acquiring the lock, so the\nthread only proceeds when the condition is genuinely satisfied. Using `if`\ninstead of `while` here is one of the most common concurrency bugs.\n",{"id":61,"difficulty":24,"q":62,"a":63},"illegal-monitor-state","When do you get IllegalMonitorStateException?","When you call `wait()`, `notify()`, or `notifyAll()` on an object **without\nholding that object's monitor** — i.e. outside a `synchronized` block\u002Fmethod\nlocked on the *same* object.\n\n```java\nObject lock = new Object();\nlock.wait();   \u002F\u002F IllegalMonitorStateException — no lock held\n\nsynchronized (lock) {\n  lock.wait(); \u002F\u002F fine — we own the monitor\n}\n```\n\nThe rule exists because these methods atomically manipulate the wait set and the\nlock, which only makes sense if the caller already owns the monitor. A frequent\nmistake is calling `notify()` on a different object than the one threads are\nwaiting on — same exception.\n",{"id":65,"difficulty":24,"q":66,"a":67},"lock-interface","What is the Lock interface and why use it over synchronized?","`java.util.concurrent.locks.Lock` is an **explicit** lock object whose API\noffers capabilities intrinsic locks lack: **`tryLock()`** (non-blocking or\ntimed), **`lockInterruptibly()`** (abandon a lock attempt on interrupt),\n**fairness** policies, and **multiple `Condition`s** per lock.\n\n```java\nLock lock = new ReentrantLock();\nlock.lock();\ntry {\n  \u002F\u002F critical section\n} finally {\n  lock.unlock();   \u002F\u002F MUST unlock in finally\n}\n```\n\nThe trade-off: it's **manual** — you must `unlock()` in a `finally`, or a thrown\nexception leaks the lock forever. Use `synchronized` for simple cases (it can't\nleak); reach for `Lock` when you need try\u002Ftimeout\u002Finterruptible acquisition or\nseparate wait conditions.\n",{"id":69,"difficulty":24,"q":70,"a":71},"reentrantlock-finally","Why must you unlock a ReentrantLock in a finally block?","Unlike `synchronized`, an explicit `Lock` is **not** released automatically when\na block exits or an exception propagates. If you `lock()` and then the body\nthrows, the lock stays held — every other thread blocks forever (a deadlock you\ncaused by leaking the lock).\n\n```java\nlock.lock();\ntry {\n  risky();          \u002F\u002F may throw\n} finally {\n  lock.unlock();    \u002F\u002F always runs, even on exception\n}\n```\n\nAlso acquire the lock **outside** the `try` (or as its first statement) so you\nnever call `unlock()` for a lock you didn't get. This boilerplate is the price\nof the extra flexibility `Lock` gives you.\n",{"id":73,"difficulty":24,"q":74,"a":75},"trylock","What does tryLock() do?","`tryLock()` attempts to acquire the lock **without blocking**: it returns `true`\nif it got the lock, `false` immediately if another thread holds it. The timed\noverload `tryLock(time, unit)` waits up to a bound, then gives up.\n\n```java\nif (lock.tryLock()) {\n  try { doWork(); } finally { lock.unlock(); }\n} else {\n  \u002F\u002F lock busy — do something else instead of blocking\n}\n\nif (lock.tryLock(2, TimeUnit.SECONDS)) { ... }  \u002F\u002F bounded wait\n```\n\nThis enables **deadlock avoidance** (back off if you can't get all needed locks)\nand **responsiveness** (don't hang indefinitely). Only call `unlock()` when\n`tryLock()` actually returned `true`.\n",{"id":77,"difficulty":14,"q":78,"a":79},"lock-interruptibly","What is lockInterruptibly() and why does it matter?","`lockInterruptibly()` acquires the lock but **responds to interruption** while\nwaiting: if another thread interrupts the blocked thread, it throws\n`InterruptedException` instead of waiting indefinitely. Plain `synchronized` and\n`lock()` are **not** interruptible — a thread stuck waiting on them can't be\ncancelled.\n\n```java\ntry {\n  lock.lockInterruptibly();\n  try { doWork(); } finally { lock.unlock(); }\n} catch (InterruptedException e) {\n  Thread.currentThread().interrupt();  \u002F\u002F restore the flag, bail out\n}\n```\n\nThis matters for **cancellable** tasks and graceful shutdown: a worker waiting\nfor a contended lock can be told to stop rather than hanging the whole system.\n",{"id":81,"difficulty":14,"q":82,"a":83},"lock-fairness","What is lock fairness and what is its trade-off?","A **fair** lock grants access in **FIFO order** — the longest-waiting thread\nacquires it next. The default `ReentrantLock` (and `synchronized`) is\n**unfair**: it may let a newly-arriving thread \"barge\" ahead of queued waiters.\n\n```java\nLock fair   = new ReentrantLock(true);  \u002F\u002F FIFO, no starvation\nLock unfair = new ReentrantLock();      \u002F\u002F default, higher throughput\n```\n\nFairness **prevents starvation** but costs **throughput**, because barging lets\nthe lock stay hot on a CPU that already has it cached and avoids context\nswitches. Use a fair lock only when you have measured starvation; otherwise the\nfaster unfair default is preferred.\n",{"id":85,"difficulty":14,"q":86,"a":87},"readwritelock","What is a ReadWriteLock and when is it useful?","A `ReadWriteLock` (impl: `ReentrantReadWriteLock`) splits one lock into a\n**read lock** and a **write lock**. Multiple threads may hold the **read lock\nsimultaneously** (reads don't conflict), but the **write lock is exclusive** —\nno other readers or writers while a writer holds it.\n\n```java\nReadWriteLock rw = new ReentrantReadWriteLock();\nrw.readLock().lock();\ntry { return cache.get(key); } finally { rw.readLock().unlock(); }\n\nrw.writeLock().lock();\ntry { cache.put(k, v); } finally { rw.writeLock().unlock(); }\n```\n\nIt shines for **read-heavy, write-rare** data (caches, config) because readers\nno longer serialize against each other. Under heavy writes it offers little over\na plain lock, and writers can starve unless you choose a fair policy.\n",{"id":89,"difficulty":14,"q":90,"a":91},"stampedlock","What is StampedLock and how does it differ from ReadWriteLock?","`StampedLock` (Java 8) is a more performant alternative offering three modes:\nwriting, reading, and a unique **optimistic read**. Each acquisition returns a\n**stamp** you pass to the release method. The optimistic read takes **no lock**\n— you read, then `validate()` the stamp to check no writer intervened.\n\n```java\nlong stamp = sl.tryOptimisticRead();   \u002F\u002F no locking\nint x = field;\nif (!sl.validate(stamp)) {              \u002F\u002F a writer slipped in?\n  stamp = sl.readLock();                \u002F\u002F fall back to a real read lock\n  try { x = field; } finally { sl.unlockRead(stamp); }\n}\n```\n\nIt's faster under read-heavy load but **not reentrant** and trickier to use\ncorrectly. Use it as a tuned replacement for `ReadWriteLock` only when profiling\njustifies the complexity.\n",{"id":93,"difficulty":14,"q":94,"a":95},"condition-vs-wait-notify","How do Condition objects compare to wait\u002Fnotify?","A `Condition` is the `Lock` equivalent of wait\u002Fnotify: `await()` parks and\nreleases the lock, `signal()`\u002F`signalAll()` wake waiters. The key advantage is\nthat **one `Lock` can have multiple `Condition`s**, so you can wait on and\nsignal **distinct predicates** separately instead of one shared wait set.\n\n```java\nLock lock = new ReentrantLock();\nCondition notFull  = lock.newCondition();\nCondition notEmpty = lock.newCondition();\n\u002F\u002F producer waits on notFull \u002F signals notEmpty; consumer the reverse\n```\n\nAs with wait, you must **hold the lock** and loop on the predicate. Separate\nconditions let a bounded buffer signal only the threads that can actually\nproceed, avoiding the \"wake everyone\" waste of a single monitor.\n",{"id":97,"difficulty":14,"q":98,"a":99},"deadlock","What is a deadlock and what four conditions cause it?","A **deadlock** is two or more threads each waiting for a lock the other holds, so\nnone can ever proceed. It requires **all four** Coffman conditions\nsimultaneously:\n\n| Condition | Meaning |\n| --------- | ------- |\n| Mutual exclusion | a resource is held exclusively |\n| Hold and wait | a thread holds one lock while requesting another |\n| No preemption | locks can't be forcibly taken away |\n| Circular wait | a cycle of threads each waiting on the next |\n\n```java\n\u002F\u002F Thread 1: synchronized(A){ synchronized(B){} }\n\u002F\u002F Thread 2: synchronized(B){ synchronized(A){} }   \u002F\u002F opposite order -> deadlock\n```\n\nBreak **any one** condition to prevent it — the practical lever is eliminating\ncircular wait via consistent lock ordering.\n",{"id":101,"difficulty":14,"q":102,"a":103},"prevent-deadlock-ordering","How do you prevent deadlock with lock ordering?","Impose a **global, consistent order** in which all threads acquire locks. If\nevery thread always grabs locks in the same sequence, a **cycle is impossible**,\nso circular wait — and thus deadlock — can't occur.\n\n```java\n\u002F\u002F order by a stable key (e.g. account id) so both threads agree\nAccount first  = a.id \u003C b.id ? a : b;\nAccount second = a.id \u003C b.id ? b : a;\nsynchronized (first) {\n  synchronized (second) { transfer(a, b); }\n}\n```\n\nOther tactics: use `tryLock` with a timeout and back off on failure, shrink lock\nscope so you hold only one lock at a time, or replace fine-grained locks with a\nsingle coarse lock. Lock ordering is the most common and reliable answer.\n",{"id":105,"difficulty":24,"q":106,"a":107},"livelock-starvation","What are livelock and starvation?","**Starvation** is when a thread is perpetually denied a resource it needs —\ne.g. low-priority threads never scheduled, or threads losing every race for an\nunfair lock. **Livelock** is when threads are **active but make no progress**:\nthey keep responding to each other and repeatedly retrying without ever\nadvancing.\n\n```java\n\u002F\u002F livelock: both keep yielding to each other, neither proceeds\nwhile (!tryLock(a) || !tryLock(b)) { unlockAll(); \u002F* retry immediately *\u002F }\n```\n\nUnlike deadlock, livelocked threads aren't blocked — they burn CPU. Fixes\ninclude **randomized back-off** before retrying, **fair locks** to cure\nstarvation, and ensuring some thread eventually wins.\n",{"id":109,"difficulty":14,"q":110,"a":111},"what-to-lock-on","What object should you synchronize on?","Prefer a **private final lock object** dedicated to guarding the state. Avoid\nlocking on `this` (callers can lock your object and interfere), on a\n**`String` literal** or `Boolean`\u002Fboxed `Integer` (interned\u002Fcached — *other*\nunrelated code may lock the same instance), or on a mutable reference (the lock\nidentity changes).\n\n```java\nprivate final Object lock = new Object();   \u002F\u002F good: encapsulated\nvoid update() { synchronized (lock) { ... } }\n```\n\nA private lock means **only your class** controls the monitor, so no external\ncode can cause deadlock or contention on it. Locking on `this` or a public field\nleaks your synchronization policy to the world.\n",{"id":113,"difficulty":24,"q":114,"a":115},"synchronized-collections","What are synchronized collections and their limits?","`Collections.synchronizedList\u002FMap\u002FSet(...)` wrap a collection so each\n**individual method** is synchronized on the wrapper. That makes single calls\nthread-safe, but **compound operations** (check-then-act, iteration) are **not**\natomic and still need external locking.\n\n```java\nList\u003CString> list = Collections.synchronizedList(new ArrayList\u003C>());\nlist.add(\"x\");                       \u002F\u002F atomic\n\nsynchronized (list) {                \u002F\u002F must lock manually to iterate\n  for (String s : list) { ... }      \u002F\u002F else ConcurrentModificationException\n}\n```\n\nThey also serialize *every* access on one lock, hurting throughput. For\nconcurrent use, prefer `java.util.concurrent` collections like\n`ConcurrentHashMap` or `CopyOnWriteArrayList`, which use finer-grained or\nlock-free strategies.\n",{"id":117,"difficulty":14,"q":118,"a":119},"lock-granularity","What is lock granularity (coarse vs fine-grained locking)?","**Granularity** is how much data a single lock protects. **Coarse-grained** —\none lock for everything — is **simple and safe** but kills concurrency, since\nunrelated operations serialize. **Fine-grained** — many small locks — allows\nmore parallelism but risks **deadlock** and adds overhead.\n\n```java\n\u002F\u002F coarse: one lock, no concurrency between buckets\nsynchronized (map) { ... }\n\n\u002F\u002F fine: lock per bucket\u002Fsegment -> independent buckets run in parallel\nsynchronized (bucketLocks[hash & MASK]) { ... }\n```\n\n`ConcurrentHashMap` is the classic example of fine-grained locking done right\n(lock striping \u002F per-bin locks). The engineering trade-off: start coarse for\ncorrectness, then split locks only where contention is **measured**, since\nfiner locks compound deadlock and complexity risk.\n",24,null,{"description":11},"Java synchronization interview questions — the synchronized keyword and intrinsic locks, the Lock interface and ReentrantLock, ReadWriteLock, deadlock, and wait\u002Fnotify coordination.","java\u002Fconcurrency\u002Fsynchronization-locks","Synchronization & Locks","Concurrency","concurrency","2026-06-20","yJCsVbBnEcJtUY69FArcqKG14k_TAZcr9aWKCrJWoA0",[131,135,136,140,144],{"subtopic":132,"path":133,"order":134},"Threads & Synchronization","\u002Fjava\u002Fconcurrency\u002Fthreads",1,{"subtopic":125,"path":20,"order":12},{"subtopic":137,"path":138,"order":139},"Executors & Thread Pools","\u002Fjava\u002Fconcurrency\u002Fexecutors-thread-pools",3,{"subtopic":141,"path":142,"order":143},"Concurrent Collections","\u002Fjava\u002Fconcurrency\u002Fconcurrent-collections",4,{"subtopic":145,"path":146,"order":147},"volatile & Memory Model","\u002Fjava\u002Fconcurrency\u002Fvolatile-memory-model",5,{"path":149,"title":150},"\u002Fblog\u002Fjava-synchronization-locks","Java Synchronization — synchronized, Locks, wait\u002Fnotify & Deadlock",1782244117319]