[{"data":1,"prerenderedAt":1502},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-synchronization-locks":3},{"id":4,"title":5,"body":6,"description":1488,"difficulty":1489,"extension":1490,"framework":1491,"frameworkSlug":72,"meta":1492,"navigation":671,"order":101,"path":1493,"qaPath":1494,"seo":1495,"stem":1496,"subtopic":1497,"topic":1498,"topicSlug":1499,"updated":1500,"__hash__":1501},"blog\u002Fblog\u002Fjava-synchronization-locks.md","Java Synchronization — synchronized, Locks, wait\u002Fnotify & Deadlock",{"type":7,"value":8,"toc":1471},"minimark",[9,14,40,44,67,122,129,133,150,201,231,235,251,312,322,326,337,380,398,402,417,461,468,472,503,589,614,618,642,693,702,706,735,806,832,836,870,937,952,955,975,1018,1033,1037,1055,1166,1198,1202,1229,1314,1320,1324,1350,1392,1402,1406,1467],[10,11,13],"h2",{"id":12},"why-synchronization-exists","Why synchronization exists",[15,16,17,18,22,23,26,27,30,31,35,36,39],"p",{},"The moment two threads touch the same mutable field and at least one of them writes,\nyour program has a ",[19,20,21],"strong",{},"race condition",": the result depends on the unpredictable timing of\nthe scheduler. Synchronization is Java's answer, and it gives you two guarantees at once —\n",[19,24,25],{},"mutual exclusion"," (only one thread in the protected region) and ",[19,28,29],{},"visibility"," (writes\nby one thread become observable to others). Miss either guarantee and you get bugs that\npass every test and then corrupt data in production at 3 a.m. This guide walks through the\nmechanisms — from the humble ",[32,33,34],"code",{},"synchronized"," keyword to explicit ",[32,37,38],{},"Lock","s — and the judgment\nthat keeps concurrent code correct.",[10,41,43],{"id":42},"race-conditions-and-the-critical-section","Race conditions and the critical section",[15,45,46,47,50,51,54,55,58,59,62,63,66],{},"A ",[19,48,49],{},"critical section"," is any region of code that reads-then-writes shared state. The\ncanonical example is ",[32,52,53],{},"count++",", which looks atomic but is really three operations: read\nthe current value, add one, write it back. Two threads can both read ",[32,56,57],{},"5",", both compute\n",[32,60,61],{},"6",", and one increment silently vanishes — a ",[19,64,65],{},"lost update",".",[68,69,74],"pre",{"className":70,"code":71,"language":72,"meta":73,"style":73},"language-java shiki shiki-themes github-light github-dark","int count = 0;\nvoid inc() { count++; }   \u002F\u002F read, add, write — three steps, not atomic\n","java","",[32,75,76,99],{"__ignoreMap":73},[77,78,81,85,89,92,96],"span",{"class":79,"line":80},"line",1,[77,82,84],{"class":83},"szBVR","int",[77,86,88],{"class":87},"sVt8B"," count ",[77,90,91],{"class":83},"=",[77,93,95],{"class":94},"sj4cs"," 0",[77,97,98],{"class":87},";\n",[77,100,102,105,109,112,115,118],{"class":79,"line":101},2,[77,103,104],{"class":83},"void",[77,106,108],{"class":107},"sScJk"," inc",[77,110,111],{"class":87},"() { count",[77,113,114],{"class":83},"++",[77,116,117],{"class":87},"; }   ",[77,119,121],{"class":120},"sJ8bj","\u002F\u002F read, add, write — three steps, not atomic\n",[15,123,124,125,128],{},"The fix is to serialize access so only one thread executes the critical section at a time.\nThe design goal is to keep that section ",[19,126,127],{},"as small as correctness allows",": hold the lock\njust long enough to mutate the shared data, no longer, so you don't needlessly block\nevery other thread.",[10,130,132],{"id":131},"the-synchronized-keyword","The synchronized keyword",[15,134,135,137,138,141,142,145,146,149],{},[32,136,34],{}," acquires an object's ",[19,139,140],{},"intrinsic lock"," (its ",[19,143,144],{},"monitor",") on entry and\nreleases it on exit — even when an exception unwinds the stack, which is why it can never\nleak a lock. While one thread holds a given monitor, every other thread that tries to\nsynchronize on the ",[19,147,148],{},"same object"," blocks until it's free.",[68,151,153],{"className":70,"code":152,"language":72,"meta":73,"style":73},"synchronized void m() { ... }          \u002F\u002F locks on 'this'\nsynchronized (lockObj) { ... }         \u002F\u002F locks on lockObj you chose\nstatic synchronized void s() { ... }   \u002F\u002F locks on the Class object (Foo.class)\n",[32,154,155,171,181],{"__ignoreMap":73},[77,156,157,159,162,165,168],{"class":79,"line":80},[77,158,34],{"class":83},[77,160,161],{"class":83}," void",[77,163,164],{"class":107}," m",[77,166,167],{"class":87},"() { ... }          ",[77,169,170],{"class":120},"\u002F\u002F locks on 'this'\n",[77,172,173,175,178],{"class":79,"line":101},[77,174,34],{"class":83},[77,176,177],{"class":87}," (lockObj) { ... }         ",[77,179,180],{"class":120},"\u002F\u002F locks on lockObj you chose\n",[77,182,184,187,190,192,195,198],{"class":79,"line":183},3,[77,185,186],{"class":83},"static",[77,188,189],{"class":83}," synchronized",[77,191,161],{"class":83},[77,193,194],{"class":107}," s",[77,196,197],{"class":87},"() { ... }   ",[77,199,200],{"class":120},"\u002F\u002F locks on the Class object (Foo.class)\n",[15,202,203,204,207,208,211,212,215,216,219,220,222,223,226,227,230],{},"A subtle trap lives in that third line: the instance monitor (",[32,205,206],{},"this",") and the ",[32,209,210],{},"Class","\nmonitor (",[32,213,214],{},"Foo.class",") are ",[19,217,218],{},"different locks",". An instance ",[32,221,34],{}," method and a\n",[32,224,225],{},"static synchronized"," method on the same class do ",[19,228,229],{},"not"," exclude each other, so mixing\nthe two without realizing it leaves a race wide open.",[10,232,234],{"id":233},"methods-vs-blocks","Methods vs blocks",[15,236,46,237,239,240,243,244,246,247,250],{},[32,238,34],{}," method locks its ",[19,241,242],{},"entire body",". A ",[32,245,34],{}," block lets you lock\nonly the portion that actually touches shared state, on ",[19,248,249],{},"any object you choose"," — which\nis almost always the better tool.",[68,252,254],{"className":70,"code":253,"language":72,"meta":73,"style":73},"void update() {\n  prepare();                  \u002F\u002F pure work — runs concurrently, no lock needed\n  synchronized (lock) {       \u002F\u002F only the critical part is serialized\n    shared.modify();\n  }\n}\n",[32,255,256,266,277,288,300,306],{"__ignoreMap":73},[77,257,258,260,263],{"class":79,"line":80},[77,259,104],{"class":83},[77,261,262],{"class":107}," update",[77,264,265],{"class":87},"() {\n",[77,267,268,271,274],{"class":79,"line":101},[77,269,270],{"class":107},"  prepare",[77,272,273],{"class":87},"();                  ",[77,275,276],{"class":120},"\u002F\u002F pure work — runs concurrently, no lock needed\n",[77,278,279,282,285],{"class":79,"line":183},[77,280,281],{"class":83},"  synchronized",[77,283,284],{"class":87}," (lock) {       ",[77,286,287],{"class":120},"\u002F\u002F only the critical part is serialized\n",[77,289,291,294,297],{"class":79,"line":290},4,[77,292,293],{"class":87},"    shared.",[77,295,296],{"class":107},"modify",[77,298,299],{"class":87},"();\n",[77,301,303],{"class":79,"line":302},5,[77,304,305],{"class":87},"  }\n",[77,307,309],{"class":79,"line":308},6,[77,310,311],{"class":87},"}\n",[15,313,314,315,318,319,321],{},"Blocks give you ",[19,316,317],{},"finer granularity",": a shorter hold time means higher concurrency. They\nalso let you avoid locking on ",[32,320,206],{},", which matters because anyone holding a reference to\nyour object can lock it and interfere with — or deadlock against — your synchronization.",[10,323,325],{"id":324},"reentrancy","Reentrancy",[15,327,328,329,332,333,336],{},"Intrinsic locks are ",[19,330,331],{},"reentrant",": a thread that already owns a lock can acquire it again\nwithout deadlocking itself. The JVM tracks an owner and a ",[19,334,335],{},"hold count","; re-entering\nincrements the count, exiting decrements it, and the lock is released only when the count\nreturns to zero.",[68,338,340],{"className":70,"code":339,"language":72,"meta":73,"style":73},"synchronized void outer() {\n  inner();          \u002F\u002F re-acquires the same 'this' lock — fine, count goes 1 -> 2\n}\nsynchronized void inner() { ... }\n",[32,341,342,353,364,368],{"__ignoreMap":73},[77,343,344,346,348,351],{"class":79,"line":80},[77,345,34],{"class":83},[77,347,161],{"class":83},[77,349,350],{"class":107}," outer",[77,352,265],{"class":87},[77,354,355,358,361],{"class":79,"line":101},[77,356,357],{"class":107},"  inner",[77,359,360],{"class":87},"();          ",[77,362,363],{"class":120},"\u002F\u002F re-acquires the same 'this' lock — fine, count goes 1 -> 2\n",[77,365,366],{"class":79,"line":183},[77,367,311],{"class":87},[77,369,370,372,374,377],{"class":79,"line":290},[77,371,34],{"class":83},[77,373,161],{"class":83},[77,375,376],{"class":107}," inner",[77,378,379],{"class":87},"() { ... }\n",[15,381,382,383,386,387,390,391,393,394,397],{},"Without reentrancy, ",[32,384,385],{},"outer()"," calling ",[32,388,389],{},"inner()"," (both locked on ",[32,392,206],{},") would block\nforever waiting on a lock it already holds. Reentrancy is what lets synchronized methods\nfreely call each other, and ",[32,395,396],{},"ReentrantLock"," provides the same semantics explicitly.",[10,399,401],{"id":400},"happens-before-the-visibility-half","Happens-before: the visibility half",[15,403,404,405,408,409,412,413,416],{},"Locks are not only about exclusion. Acquiring and releasing a monitor establishes a\n",[19,406,407],{},"happens-before"," relationship: everything a thread does ",[19,410,411],{},"before releasing"," a lock is\nguaranteed visible to any thread that ",[19,414,415],{},"subsequently acquires the same lock",". The release\nflushes writes toward main memory; the acquire invalidates stale cached reads.",[68,418,420],{"className":70,"code":419,"language":72,"meta":73,"style":73},"synchronized (lock) { data = 42; }     \u002F\u002F write happens-before the release\n\u002F\u002F ... another thread, later:\nsynchronized (lock) { use(data); }     \u002F\u002F acquire sees the prior release -> reads 42\n",[32,421,422,440,445],{"__ignoreMap":73},[77,423,424,426,429,431,434,437],{"class":79,"line":80},[77,425,34],{"class":83},[77,427,428],{"class":87}," (lock) { data ",[77,430,91],{"class":83},[77,432,433],{"class":94}," 42",[77,435,436],{"class":87},"; }     ",[77,438,439],{"class":120},"\u002F\u002F write happens-before the release\n",[77,441,442],{"class":79,"line":101},[77,443,444],{"class":120},"\u002F\u002F ... another thread, later:\n",[77,446,447,449,452,455,458],{"class":79,"line":183},[77,448,34],{"class":83},[77,450,451],{"class":87}," (lock) { ",[77,453,454],{"class":107},"use",[77,456,457],{"class":87},"(data); }     ",[77,459,460],{"class":120},"\u002F\u002F acquire sees the prior release -> reads 42\n",[15,462,463,464,467],{},"The catch: ",[19,465,466],{},"both sides must use the same lock",". Synchronizing your writes but reading\nthe field without the lock breaks the chain — the reader may see a stale value forever,\nbecause the CPU is free to cache and reorder without a happens-before edge. This is the\nreason \"I only synchronized the writer\" is never a real fix.",[10,469,471],{"id":470},"coordinating-threads-with-waitnotify","Coordinating threads with wait\u002Fnotify",[15,473,474,475,479,480,483,484,487,488,491,492,495,496,498,499,502],{},"When a thread needs to ",[476,477,478],"em",{},"wait for a condition"," another thread will produce, it uses the\nmonitor's wait set. ",[32,481,482],{},"wait()"," ",[19,485,486],{},"releases the lock"," and parks the thread; another thread\ncalls ",[32,489,490],{},"notify()"," (wakes one) or ",[32,493,494],{},"notifyAll()"," (wakes all) on the ",[19,497,148],{},", and the\nwoken thread must ",[19,500,501],{},"re-acquire the lock"," before it continues.",[68,504,506],{"className":70,"code":505,"language":72,"meta":73,"style":73},"synchronized (queue) {\n  while (queue.isEmpty())   \u002F\u002F guard in a loop — see next section\n    queue.wait();           \u002F\u002F releases the monitor, sleeps until notified\n  process(queue.remove());\n}\n\u002F\u002F producer:\nsynchronized (queue) { queue.add(x); queue.notifyAll(); }\n",[32,507,508,515,532,546,560,564,569],{"__ignoreMap":73},[77,509,510,512],{"class":79,"line":80},[77,511,34],{"class":83},[77,513,514],{"class":87}," (queue) {\n",[77,516,517,520,523,526,529],{"class":79,"line":101},[77,518,519],{"class":83},"  while",[77,521,522],{"class":87}," (queue.",[77,524,525],{"class":107},"isEmpty",[77,527,528],{"class":87},"())   ",[77,530,531],{"class":120},"\u002F\u002F guard in a loop — see next section\n",[77,533,534,537,540,543],{"class":79,"line":183},[77,535,536],{"class":87},"    queue.",[77,538,539],{"class":107},"wait",[77,541,542],{"class":87},"();           ",[77,544,545],{"class":120},"\u002F\u002F releases the monitor, sleeps until notified\n",[77,547,548,551,554,557],{"class":79,"line":290},[77,549,550],{"class":107},"  process",[77,552,553],{"class":87},"(queue.",[77,555,556],{"class":107},"remove",[77,558,559],{"class":87},"());\n",[77,561,562],{"class":79,"line":302},[77,563,311],{"class":87},[77,565,566],{"class":79,"line":308},[77,567,568],{"class":120},"\u002F\u002F producer:\n",[77,570,572,574,577,580,583,586],{"class":79,"line":571},7,[77,573,34],{"class":83},[77,575,576],{"class":87}," (queue) { queue.",[77,578,579],{"class":107},"add",[77,581,582],{"class":87},"(x); queue.",[77,584,585],{"class":107},"notifyAll",[77,587,588],{"class":87},"(); }\n",[15,590,591,592,595,596,598,599,602,603,605,606,609,610,613],{},"You ",[19,593,594],{},"must hold the monitor"," to call ",[32,597,539],{},", ",[32,600,601],{},"notify",", or ",[32,604,585],{}," — otherwise you get\n",[32,607,608],{},"IllegalMonitorStateException",". That rule exists because these methods atomically\nmanipulate both the lock and the wait set, which is only meaningful if you already own the\nmonitor. A common variant of the bug is notifying on a ",[476,611,612],{},"different"," object than the one the\nwaiters parked on.",[10,615,617],{"id":616},"why-wait-lives-in-a-while-loop","Why wait() lives in a while loop",[15,619,620,621,623,624,627,628,630,631,634,635,637,638,641],{},"Never guard ",[32,622,482],{}," with an ",[32,625,626],{},"if",". A thread can return from ",[32,629,482],{}," even when the\ncondition is still false, for two reasons: ",[19,632,633],{},"spurious wakeups"," (the JVM is permitted to\nwake a thread for no reason at all) and the fact that ",[32,636,494],{}," wakes ",[19,639,640],{},"multiple","\nthreads that then race — by the time a given thread re-acquires the lock, another may have\nalready consumed the thing it was waiting for.",[68,643,645],{"className":70,"code":644,"language":72,"meta":73,"style":73},"\u002F\u002F WRONG — assumes the predicate holds after waking\nif (queue.isEmpty()) queue.wait();\n\n\u002F\u002F RIGHT — re-tests the predicate after every wakeup\nwhile (queue.isEmpty()) queue.wait();\n",[32,646,647,652,667,673,678],{"__ignoreMap":73},[77,648,649],{"class":79,"line":80},[77,650,651],{"class":120},"\u002F\u002F WRONG — assumes the predicate holds after waking\n",[77,653,654,656,658,660,663,665],{"class":79,"line":101},[77,655,626],{"class":83},[77,657,522],{"class":87},[77,659,525],{"class":107},[77,661,662],{"class":87},"()) queue.",[77,664,539],{"class":107},[77,666,299],{"class":87},[77,668,669],{"class":79,"line":183},[77,670,672],{"emptyLinePlaceholder":671},true,"\n",[77,674,675],{"class":79,"line":290},[77,676,677],{"class":120},"\u002F\u002F RIGHT — re-tests the predicate after every wakeup\n",[77,679,680,683,685,687,689,691],{"class":79,"line":302},[77,681,682],{"class":83},"while",[77,684,522],{"class":87},[77,686,525],{"class":107},[77,688,662],{"class":87},[77,690,539],{"class":107},[77,692,299],{"class":87},[15,694,695,696,698,699,701],{},"The ",[32,697,682],{}," loop re-checks the predicate after re-acquiring the lock, so the thread\nproceeds only when the condition is genuinely true. Using ",[32,700,626],{}," here is one of the most\nfrequently shipped concurrency bugs.",[10,703,705],{"id":704},"lock-vs-synchronized","Lock vs synchronized",[15,707,695,708,711,712,714,715,718,719,722,723,726,727,730,731,734],{},[32,709,710],{},"java.util.concurrent.locks.Lock"," interface (chiefly ",[32,713,396],{},") is an\n",[19,716,717],{},"explicit"," lock object offering capabilities intrinsic locks can't: non-blocking and\ntimed acquisition (",[32,720,721],{},"tryLock","), interruptible acquisition (",[32,724,725],{},"lockInterruptibly","),\nconfigurable ",[19,728,729],{},"fairness",", and multiple ",[32,732,733],{},"Condition","s per lock. The price is that it's\nmanual — you must release it yourself.",[68,736,738],{"className":70,"code":737,"language":72,"meta":73,"style":73},"Lock lock = new ReentrantLock();\nlock.lock();\ntry {\n  \u002F\u002F critical section\n} finally {\n  lock.unlock();   \u002F\u002F MUST be in finally — Lock is NOT auto-released\n}\n",[32,739,740,755,765,773,778,788,802],{"__ignoreMap":73},[77,741,742,745,747,750,753],{"class":79,"line":80},[77,743,744],{"class":87},"Lock lock ",[77,746,91],{"class":83},[77,748,749],{"class":83}," new",[77,751,752],{"class":107}," ReentrantLock",[77,754,299],{"class":87},[77,756,757,760,763],{"class":79,"line":101},[77,758,759],{"class":87},"lock.",[77,761,762],{"class":107},"lock",[77,764,299],{"class":87},[77,766,767,770],{"class":79,"line":183},[77,768,769],{"class":83},"try",[77,771,772],{"class":87}," {\n",[77,774,775],{"class":79,"line":290},[77,776,777],{"class":120},"  \u002F\u002F critical section\n",[77,779,780,783,786],{"class":79,"line":302},[77,781,782],{"class":87},"} ",[77,784,785],{"class":83},"finally",[77,787,772],{"class":87},[77,789,790,793,796,799],{"class":79,"line":308},[77,791,792],{"class":87},"  lock.",[77,794,795],{"class":107},"unlock",[77,797,798],{"class":87},"();   ",[77,800,801],{"class":120},"\u002F\u002F MUST be in finally — Lock is NOT auto-released\n",[77,803,804],{"class":79,"line":571},[77,805,311],{"class":87},[15,807,808,809,811,812,814,815,818,819,821,822,825,826,828,829,831],{},"Unlike ",[32,810,34],{},", an explicit lock is not freed when a block exits or an exception\npropagates. Omit the ",[32,813,785],{}," and a thrown exception leaks the lock forever, deadlocking\nevery other thread. Acquire the lock ",[19,816,817],{},"outside"," the ",[32,820,769],{}," (or as its first statement) so\nyou never ",[32,823,824],{},"unlock()"," a lock you didn't get. Rule: use ",[32,827,34],{}," for simple mutual\nexclusion; reach for ",[32,830,38],{}," only when you need its extra powers.",[10,833,835],{"id":834},"trylock-timeouts-and-interruptible-acquisition","tryLock, timeouts, and interruptible acquisition",[15,837,838,839,842,843,846,847,850,851,854,855,858,859,862,863,866,867,869],{},"Two of those extra powers directly attack hangs and deadlock. ",[32,840,841],{},"tryLock()"," grabs the lock\n",[19,844,845],{},"without blocking"," — returning ",[32,848,849],{},"true"," on success, ",[32,852,853],{},"false"," immediately if it's held — and\nthe timed overload waits up to a bound before giving up. ",[32,856,857],{},"lockInterruptibly()"," lets a\nwaiting thread be ",[19,860,861],{},"cancelled"," via interruption instead of hanging indefinitely; plain\n",[32,864,865],{},"lock()"," and ",[32,868,34],{}," are not interruptible.",[68,871,873],{"className":70,"code":872,"language":72,"meta":73,"style":73},"if (lock.tryLock(2, TimeUnit.SECONDS)) {   \u002F\u002F bounded wait, then back off\n  try { doWork(); } finally { lock.unlock(); }\n} else {\n  \u002F\u002F couldn't get it — retry, log, or take an alternative path\n}\n",[32,874,875,896,919,928,933],{"__ignoreMap":73},[77,876,877,879,882,884,887,890,893],{"class":79,"line":80},[77,878,626],{"class":83},[77,880,881],{"class":87}," (lock.",[77,883,721],{"class":107},[77,885,886],{"class":87},"(",[77,888,889],{"class":94},"2",[77,891,892],{"class":87},", TimeUnit.SECONDS)) {   ",[77,894,895],{"class":120},"\u002F\u002F bounded wait, then back off\n",[77,897,898,901,904,907,910,912,915,917],{"class":79,"line":101},[77,899,900],{"class":83},"  try",[77,902,903],{"class":87}," { ",[77,905,906],{"class":107},"doWork",[77,908,909],{"class":87},"(); } ",[77,911,785],{"class":83},[77,913,914],{"class":87}," { lock.",[77,916,795],{"class":107},[77,918,588],{"class":87},[77,920,921,923,926],{"class":79,"line":183},[77,922,782],{"class":87},[77,924,925],{"class":83},"else",[77,927,772],{"class":87},[77,929,930],{"class":79,"line":290},[77,931,932],{"class":120},"  \u002F\u002F couldn't get it — retry, log, or take an alternative path\n",[77,934,935],{"class":79,"line":302},[77,936,311],{"class":87},[15,938,939,941,942,945,946,948,949,951],{},[32,940,721],{}," enables ",[19,943,944],{},"deadlock avoidance",": if a thread can't acquire every lock it needs,\nit releases what it has and backs off rather than waiting in a cycle. ",[32,947,725],{},"\nenables graceful shutdown of workers stuck on contended locks. Only call ",[32,950,824],{}," when\nacquisition actually succeeded.",[10,953,954],{"id":729},"Fairness",[15,956,46,957,960,961,964,965,967,968,970,971,974],{},[19,958,959],{},"fair"," lock hands ownership to the ",[19,962,963],{},"longest-waiting"," thread (FIFO order). The\ndefault ",[32,966,396],{},", like ",[32,969,34],{},", is ",[19,972,973],{},"unfair",": a newly arriving thread may\n\"barge\" ahead of queued waiters.",[68,976,978],{"className":70,"code":977,"language":72,"meta":73,"style":73},"Lock fair   = new ReentrantLock(true);  \u002F\u002F FIFO order, no starvation\nLock unfair = new ReentrantLock();      \u002F\u002F default — higher throughput\n",[32,979,980,1001],{"__ignoreMap":73},[77,981,982,985,987,989,991,993,995,998],{"class":79,"line":80},[77,983,984],{"class":87},"Lock fair   ",[77,986,91],{"class":83},[77,988,749],{"class":83},[77,990,752],{"class":107},[77,992,886],{"class":87},[77,994,849],{"class":94},[77,996,997],{"class":87},");  ",[77,999,1000],{"class":120},"\u002F\u002F FIFO order, no starvation\n",[77,1002,1003,1006,1008,1010,1012,1015],{"class":79,"line":101},[77,1004,1005],{"class":87},"Lock unfair ",[77,1007,91],{"class":83},[77,1009,749],{"class":83},[77,1011,752],{"class":107},[77,1013,1014],{"class":87},"();      ",[77,1016,1017],{"class":120},"\u002F\u002F default — higher throughput\n",[15,1019,1020,1021,1024,1025,1028,1029,1032],{},"Fairness prevents ",[19,1022,1023],{},"starvation"," but costs ",[19,1026,1027],{},"throughput",", because barging keeps the lock\nhot on a CPU that already has it cached and avoids context switches. Choose fairness only\nafter you've ",[476,1030,1031],{},"measured"," starvation; otherwise the faster unfair default wins.",[10,1034,1036],{"id":1035},"readwritelock-and-condition","ReadWriteLock and Condition",[15,1038,1039,1040,1043,1044,1047,1048,1051,1052,66],{},"When data is ",[19,1041,1042],{},"read often and written rarely",", a single mutual-exclusion lock wastes\nparallelism by serializing readers against each other. A ",[32,1045,1046],{},"ReadWriteLock"," splits the lock\nin two: many threads may hold the ",[19,1049,1050],{},"read lock"," at once, but the ",[19,1053,1054],{},"write lock is\nexclusive",[68,1056,1058],{"className":70,"code":1057,"language":72,"meta":73,"style":73},"ReadWriteLock rw = new ReentrantReadWriteLock();\nrw.readLock().lock();\ntry { return cache.get(key); }  finally { rw.readLock().unlock(); }   \u002F\u002F shared\nrw.writeLock().lock();\ntry { cache.put(k, v); }        finally { rw.writeLock().unlock(); }  \u002F\u002F exclusive\n",[32,1059,1060,1074,1089,1124,1137],{"__ignoreMap":73},[77,1061,1062,1065,1067,1069,1072],{"class":79,"line":80},[77,1063,1064],{"class":87},"ReadWriteLock rw ",[77,1066,91],{"class":83},[77,1068,749],{"class":83},[77,1070,1071],{"class":107}," ReentrantReadWriteLock",[77,1073,299],{"class":87},[77,1075,1076,1079,1082,1085,1087],{"class":79,"line":101},[77,1077,1078],{"class":87},"rw.",[77,1080,1081],{"class":107},"readLock",[77,1083,1084],{"class":87},"().",[77,1086,762],{"class":107},[77,1088,299],{"class":87},[77,1090,1091,1093,1095,1098,1101,1104,1107,1109,1112,1114,1116,1118,1121],{"class":79,"line":183},[77,1092,769],{"class":83},[77,1094,903],{"class":87},[77,1096,1097],{"class":83},"return",[77,1099,1100],{"class":87}," cache.",[77,1102,1103],{"class":107},"get",[77,1105,1106],{"class":87},"(key); }  ",[77,1108,785],{"class":83},[77,1110,1111],{"class":87}," { rw.",[77,1113,1081],{"class":107},[77,1115,1084],{"class":87},[77,1117,795],{"class":107},[77,1119,1120],{"class":87},"(); }   ",[77,1122,1123],{"class":120},"\u002F\u002F shared\n",[77,1125,1126,1128,1131,1133,1135],{"class":79,"line":290},[77,1127,1078],{"class":87},[77,1129,1130],{"class":107},"writeLock",[77,1132,1084],{"class":87},[77,1134,762],{"class":107},[77,1136,299],{"class":87},[77,1138,1139,1141,1144,1147,1150,1152,1154,1156,1158,1160,1163],{"class":79,"line":302},[77,1140,769],{"class":83},[77,1142,1143],{"class":87}," { cache.",[77,1145,1146],{"class":107},"put",[77,1148,1149],{"class":87},"(k, v); }        ",[77,1151,785],{"class":83},[77,1153,1111],{"class":87},[77,1155,1130],{"class":107},[77,1157,1084],{"class":87},[77,1159,795],{"class":107},[77,1161,1162],{"class":87},"(); }  ",[77,1164,1165],{"class":120},"\u002F\u002F exclusive\n",[15,1167,46,1168,1170,1171,1174,1175,1179,1180,1183,1184,1187,1188,1191,1192,1194,1195,66],{},[32,1169,38],{}," also replaces ",[32,1172,1173],{},"wait\u002Fnotify"," with ",[19,1176,1177],{},[32,1178,733],{}," objects, and crucially ",[19,1181,1182],{},"one\nlock can own several conditions",". A bounded buffer can keep a ",[32,1185,1186],{},"notFull"," and a ",[32,1189,1190],{},"notEmpty","\ncondition, signalling only the threads that can actually proceed instead of waking\neveryone. As with ",[32,1193,539],{},", you must hold the lock and loop on the predicate when calling\n",[32,1196,1197],{},"await()",[10,1199,1201],{"id":1200},"deadlock-and-how-to-prevent-it","Deadlock and how to prevent it",[15,1203,46,1204,1207,1208,1211,1212,1214,1215,598,1218,1221,1222,1225,1226,66],{},[19,1205,1206],{},"deadlock"," is two or more threads each holding a lock the other needs, so none ever\nproceeds. It requires ",[19,1209,1210],{},"all four"," Coffman conditions at once: ",[19,1213,25],{},",\n",[19,1216,1217],{},"hold-and-wait",[19,1219,1220],{},"no preemption",", and ",[19,1223,1224],{},"circular wait",". Break any one and deadlock is\nimpossible — and the practical lever is the last one. Eliminate circular wait by imposing a\n",[19,1227,1228],{},"global, consistent lock-acquisition order",[68,1230,1232],{"className":70,"code":1231,"language":72,"meta":73,"style":73},"\u002F\u002F Both threads grab locks in the same order keyed on a stable id — no cycle possible\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",[32,1233,1234,1239,1267,1290,1297,1310],{"__ignoreMap":73},[77,1235,1236],{"class":79,"line":80},[77,1237,1238],{"class":120},"\u002F\u002F Both threads grab locks in the same order keyed on a stable id — no cycle possible\n",[77,1240,1241,1244,1246,1249,1252,1255,1258,1261,1264],{"class":79,"line":101},[77,1242,1243],{"class":87},"Account first  ",[77,1245,91],{"class":83},[77,1247,1248],{"class":87}," a.id ",[77,1250,1251],{"class":83},"\u003C",[77,1253,1254],{"class":87}," b.id ",[77,1256,1257],{"class":83},"?",[77,1259,1260],{"class":87}," a ",[77,1262,1263],{"class":83},":",[77,1265,1266],{"class":87}," b;\n",[77,1268,1269,1272,1274,1276,1278,1280,1282,1285,1287],{"class":79,"line":183},[77,1270,1271],{"class":87},"Account second ",[77,1273,91],{"class":83},[77,1275,1248],{"class":87},[77,1277,1251],{"class":83},[77,1279,1254],{"class":87},[77,1281,1257],{"class":83},[77,1283,1284],{"class":87}," b ",[77,1286,1263],{"class":83},[77,1288,1289],{"class":87}," a;\n",[77,1291,1292,1294],{"class":79,"line":290},[77,1293,34],{"class":83},[77,1295,1296],{"class":87}," (first) {\n",[77,1298,1299,1301,1304,1307],{"class":79,"line":302},[77,1300,281],{"class":83},[77,1302,1303],{"class":87}," (second) { ",[77,1305,1306],{"class":107},"transfer",[77,1308,1309],{"class":87},"(a, b); }\n",[77,1311,1312],{"class":79,"line":308},[77,1313,311],{"class":87},[15,1315,1316,1317,1319],{},"If every thread always acquires locks in the same sequence, a waiting cycle cannot form.\nOther tactics — timed ",[32,1318,721],{}," with back-off, shrinking scope so you hold one lock at a\ntime, or collapsing to a single coarse lock — all help, but consistent lock ordering is\nthe most reliable answer.",[10,1321,1323],{"id":1322},"what-to-lock-on","What to lock on",[15,1325,1326,1327,1330,1331,1333,1334,1337,1338,1341,1342,1345,1346,1349],{},"Prefer a ",[19,1328,1329],{},"private final lock object"," dedicated to guarding your state. Don't lock on\n",[32,1332,206],{}," (external callers can lock your object and interfere), and never lock on a ",[32,1335,1336],{},"String","\nliteral or a boxed ",[32,1339,1340],{},"Boolean","\u002F",[32,1343,1344],{},"Integer"," — those are interned or cached, so ",[476,1347,1348],{},"unrelated"," code\nmay lock the very same instance. Don't lock on a reference you reassign, either: change the\nreference and the lock identity changes underneath you.",[68,1351,1353],{"className":70,"code":1352,"language":72,"meta":73,"style":73},"private final Object lock = new Object();   \u002F\u002F encapsulated, immutable identity\nvoid update() { synchronized (lock) { ... } }\n",[32,1354,1355,1378],{"__ignoreMap":73},[77,1356,1357,1360,1363,1366,1368,1370,1373,1375],{"class":79,"line":80},[77,1358,1359],{"class":83},"private",[77,1361,1362],{"class":83}," final",[77,1364,1365],{"class":87}," Object lock ",[77,1367,91],{"class":83},[77,1369,749],{"class":83},[77,1371,1372],{"class":107}," Object",[77,1374,798],{"class":87},[77,1376,1377],{"class":120},"\u002F\u002F encapsulated, immutable identity\n",[77,1379,1380,1382,1384,1387,1389],{"class":79,"line":101},[77,1381,104],{"class":83},[77,1383,262],{"class":107},[77,1385,1386],{"class":87},"() { ",[77,1388,34],{"class":83},[77,1390,1391],{"class":87}," (lock) { ... } }\n",[15,1393,1394,1395,1398,1399,1401],{},"A private lock means ",[19,1396,1397],{},"only your class"," controls that monitor, so no outside code can\ncontend on it or create a deadlock through it. Locking on ",[32,1400,206],{}," or a public field leaks\nyour synchronization policy to the whole program.",[10,1403,1405],{"id":1404},"recap","Recap",[15,1407,1408,1409,1411,1412,1415,1416,1418,1419,1421,1422,1424,1425,1435,1436,1440,1441,598,1443,1445,1446,1448,1449,1451,1452,1454,1455,1459,1460,1462,1463,1466],{},"Synchronization buys you two things together — ",[19,1410,25],{}," to stop race\nconditions and ",[19,1413,1414],{},"happens-before visibility"," so writes are actually seen. ",[32,1417,34],{},"\nis the simple, leak-proof, reentrant default that locks an object's intrinsic ",[19,1420,144],{},";\njust remember instance and ",[32,1423,210],{}," locks are distinct. Coordinate threads with\n",[19,1426,1427,1341,1429,1431,1432,1434],{},[32,1428,539],{},[32,1430,601],{}," inside a ",[32,1433,682],{}," loop"," to survive spurious wakeups. Step up to\n",[19,1437,1438],{},[32,1439,396],{}," when you need ",[32,1442,721],{},[32,1444,725],{},", fairness, or multiple\n",[32,1447,733],{},"s — and always ",[32,1450,824],{}," in a ",[32,1453,785],{},". Use ",[19,1456,1457],{},[32,1458,1046],{}," for\nread-heavy data, prevent ",[19,1461,1206],{}," with consistent lock ordering, and always\nsynchronize on a ",[19,1464,1465],{},"private final object",". Master these and concurrent Java stops being a\nguessing game.",[1468,1469,1470],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":73,"searchDepth":101,"depth":101,"links":1472},[1473,1474,1475,1476,1477,1478,1479,1480,1481,1482,1483,1484,1485,1486,1487],{"id":12,"depth":101,"text":13},{"id":42,"depth":101,"text":43},{"id":131,"depth":101,"text":132},{"id":233,"depth":101,"text":234},{"id":324,"depth":101,"text":325},{"id":400,"depth":101,"text":401},{"id":470,"depth":101,"text":471},{"id":616,"depth":101,"text":617},{"id":704,"depth":101,"text":705},{"id":834,"depth":101,"text":835},{"id":729,"depth":101,"text":954},{"id":1035,"depth":101,"text":1036},{"id":1200,"depth":101,"text":1201},{"id":1322,"depth":101,"text":1323},{"id":1404,"depth":101,"text":1405},"How Java synchronization actually works — the synchronized keyword and intrinsic monitors, happens-before visibility, wait\u002Fnotify, ReentrantLock vs synchronized, ReadWriteLock, Condition, and how to avoid deadlock.","hard","md","Java",{},"\u002Fblog\u002Fjava-synchronization-locks","\u002Fjava\u002Fconcurrency\u002Fsynchronization-locks",{"title":5,"description":1488},"blog\u002Fjava-synchronization-locks","Synchronization & Locks","Concurrency","concurrency","2026-06-20","MaXvbl129ZH0-Hh998kCxx3LCTlS7e7PKJhPGt9gOKU",1782244089950]