[{"data":1,"prerenderedAt":1360},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-volatile-memory-model":3},{"id":4,"title":5,"body":6,"description":1346,"difficulty":1347,"extension":1348,"framework":1349,"frameworkSlug":60,"meta":1350,"navigation":109,"order":125,"path":1351,"qaPath":1352,"seo":1353,"stem":1354,"subtopic":1355,"topic":1356,"topicSlug":1357,"updated":1358,"__hash__":1359},"blog\u002Fblog\u002Fjava-volatile-memory-model.md","Java volatile & the Java Memory Model — Visibility, happens-before & Safe Publication",{"type":7,"value":8,"toc":1331},"minimark",[9,14,40,44,55,181,201,205,219,239,252,256,269,309,320,324,341,452,465,469,472,498,534,559,563,585,655,675,679,694,752,769,773,798,936,948,952,966,1037,1066,1070,1086,1166,1182,1186,1213,1238,1255,1259,1327],[10,11,13],"h2",{"id":12},"why-concurrency-breaks-your-intuition","Why concurrency breaks your intuition",[15,16,17,18,22,23,26,27,31,32,35,36,39],"p",{},"Single-threaded Java behaves the way you read it: statements run top to bottom, a write you\njust made is the value you read back. The moment a second thread touches the same fields,\nthat intuition collapses. A write made by one thread might never become visible to another;\ninstructions you wrote in one order might appear to run in another. None of this is a JVM\nbug — it is the ",[19,20,21],"strong",{},"Java Memory Model (JMM)"," deliberately leaving the JVM, JIT, and CPU free\nto optimize. To write correct concurrent code you stop reasoning about \"what the CPU does\"\nand start reasoning about the JMM's one core abstraction: ",[19,24,25],{},"happens-before",". This guide\nbuilds up that model and shows where ",[28,29,30],"code",{},"volatile",", ",[28,33,34],{},"synchronized",", and ",[28,37,38],{},"final"," fit.",[10,41,43],{"id":42},"the-visibility-problem","The visibility problem",[15,45,46,47,50,51,54],{},"The classic motivating bug is a flag loop that never stops. One thread spins on a boolean;\nanother sets it to ",[28,48,49],{},"true","; the spinning thread keeps going forever. Nothing is \"wrong\" with\nthe code — the reader is simply allowed to keep the flag in a ",[19,52,53],{},"register or CPU cache",", or\nthe JIT may hoist the read out of the loop entirely, so it never re-reads main memory.",[56,57,62],"pre",{"className":58,"code":59,"language":60,"meta":61,"style":61},"language-java shiki shiki-themes github-light github-dark","class Worker {\n  boolean stop = false;          \u002F\u002F plain field — NOT volatile\n\n  void run() {\n    while (!stop) { \u002F* busy *\u002F } \u002F\u002F JIT may read stop ONCE, then loop on a cached copy\n  }\n  void shutdown() { stop = true; } \u002F\u002F a different thread sets it — write may never be seen\n}\n","java","",[28,63,64,81,104,111,123,147,153,175],{"__ignoreMap":61},[65,66,69,73,77],"span",{"class":67,"line":68},"line",1,[65,70,72],{"class":71},"szBVR","class",[65,74,76],{"class":75},"sScJk"," Worker",[65,78,80],{"class":79},"sVt8B"," {\n",[65,82,84,87,90,93,97,100],{"class":67,"line":83},2,[65,85,86],{"class":71},"  boolean",[65,88,89],{"class":79}," stop ",[65,91,92],{"class":71},"=",[65,94,96],{"class":95},"sj4cs"," false",[65,98,99],{"class":79},";          ",[65,101,103],{"class":102},"sJ8bj","\u002F\u002F plain field — NOT volatile\n",[65,105,107],{"class":67,"line":106},3,[65,108,110],{"emptyLinePlaceholder":109},true,"\n",[65,112,114,117,120],{"class":67,"line":113},4,[65,115,116],{"class":71},"  void",[65,118,119],{"class":75}," run",[65,121,122],{"class":79},"() {\n",[65,124,126,129,132,135,138,141,144],{"class":67,"line":125},5,[65,127,128],{"class":71},"    while",[65,130,131],{"class":79}," (",[65,133,134],{"class":71},"!",[65,136,137],{"class":79},"stop) { ",[65,139,140],{"class":102},"\u002F* busy *\u002F",[65,142,143],{"class":79}," } ",[65,145,146],{"class":102},"\u002F\u002F JIT may read stop ONCE, then loop on a cached copy\n",[65,148,150],{"class":67,"line":149},6,[65,151,152],{"class":79},"  }\n",[65,154,156,158,161,164,166,169,172],{"class":67,"line":155},7,[65,157,116],{"class":71},[65,159,160],{"class":75}," shutdown",[65,162,163],{"class":79},"() { stop ",[65,165,92],{"class":71},[65,167,168],{"class":95}," true",[65,170,171],{"class":79},"; } ",[65,173,174],{"class":102},"\u002F\u002F a different thread sets it — write may never be seen\n",[65,176,178],{"class":67,"line":177},8,[65,179,180],{"class":79},"}\n",[15,182,183,184,187,188,191,195,196,200],{},"A ",[19,185,186],{},"write being invisible"," is the heart of the problem: without a synchronization action,\nthe JMM gives no promise that one thread's writes ever reach another. Marking ",[28,189,190],{},"stop",[19,192,193],{},[28,194,30],{}," is the smallest fix, but to understand ",[197,198,199],"em",{},"why"," it works you need the model\nunderneath it.",[10,202,204],{"id":203},"what-the-jmm-is-and-why-it-exists","What the JMM is and why it exists",[15,206,207,208,211,212,35,215,218],{},"The ",[19,209,210],{},"Java Memory Model"," is the slice of the language spec that answers two questions:\n",[197,213,214],{},"which writes is a thread guaranteed to see",[197,216,217],{},"what reorderings are legal",". It has to\nexist because real systems optimize aggressively — store buffers, multi-level caches,\nout-of-order execution, register allocation, JIT reordering. Without a portable contract,\nthe same program would behave differently on every CPU and be impossible to reason about.",[56,220,222],{"className":58,"code":221,"language":60,"meta":61,"style":61},"\u002F\u002F The JMM does NOT promise a single global order of operations across threads.\n\u002F\u002F It promises a MINIMUM set of guarantees you can build on:\n\u002F\u002F   volatile, synchronized, final, Thread.start()\u002Fjoin(), and a few library tools.\n",[28,223,224,229,234],{"__ignoreMap":61},[65,225,226],{"class":67,"line":68},[65,227,228],{"class":102},"\u002F\u002F The JMM does NOT promise a single global order of operations across threads.\n",[65,230,231],{"class":67,"line":83},[65,232,233],{"class":102},"\u002F\u002F It promises a MINIMUM set of guarantees you can build on:\n",[65,235,236],{"class":67,"line":106},[65,237,238],{"class":102},"\u002F\u002F   volatile, synchronized, final, Thread.start()\u002Fjoin(), and a few library tools.\n",[15,240,241,242,245,246,248,249,251],{},"Crucially, the JMM does ",[19,243,244],{},"not"," hand you a single tidy timeline of every operation. It\ngives you a partial order — ",[19,247,25],{}," — and tells you that anything ",[197,250,244],{}," ordered\nby it may be reordered or read stale. Your job is to deliberately place happens-before edges\nwhere threads communicate.",[10,253,255],{"id":254},"the-happens-before-relationship","The happens-before relationship",[15,257,258,261,262,264,265,268],{},[19,259,260],{},"Happens-before"," is the JMM's promise: if action A ",[197,263,25],{}," B, then everything A\nwrote is visible to B, and A appears to run before B. The edges come from a fixed set of\nrules, and ",[19,266,267],{},"transitivity"," chains them together:",[56,270,272],{"className":58,"code":271,"language":60,"meta":61,"style":61},"\u002F\u002F Sources of happens-before edges:\n\u002F\u002F   Program order  — each action hb later actions in the SAME thread\n\u002F\u002F   Monitor lock   — unlocking a monitor hb a later lock of the SAME monitor\n\u002F\u002F   Volatile       — a write to a volatile field hb every later read of it\n\u002F\u002F   Thread start   — t.start() hb every action inside the started thread\n\u002F\u002F   Thread join    — every action in a thread hb another thread's t.join() returning\n\u002F\u002F   Transitivity   — if A hb B and B hb C, then A hb C\n",[28,273,274,279,284,289,294,299,304],{"__ignoreMap":61},[65,275,276],{"class":67,"line":68},[65,277,278],{"class":102},"\u002F\u002F Sources of happens-before edges:\n",[65,280,281],{"class":67,"line":83},[65,282,283],{"class":102},"\u002F\u002F   Program order  — each action hb later actions in the SAME thread\n",[65,285,286],{"class":67,"line":106},[65,287,288],{"class":102},"\u002F\u002F   Monitor lock   — unlocking a monitor hb a later lock of the SAME monitor\n",[65,290,291],{"class":67,"line":113},[65,292,293],{"class":102},"\u002F\u002F   Volatile       — a write to a volatile field hb every later read of it\n",[65,295,296],{"class":67,"line":125},[65,297,298],{"class":102},"\u002F\u002F   Thread start   — t.start() hb every action inside the started thread\n",[65,300,301],{"class":67,"line":149},[65,302,303],{"class":102},"\u002F\u002F   Thread join    — every action in a thread hb another thread's t.join() returning\n",[65,305,306],{"class":67,"line":155},[65,307,308],{"class":102},"\u002F\u002F   Transitivity   — if A hb B and B hb C, then A hb C\n",[15,310,311,312,315,316,319],{},"Notice what is missing: two plain reads\u002Fwrites in ",[197,313,314],{},"different"," threads have ",[19,317,318],{},"no"," edge\nbetween them by default. Where there is no happens-before edge, the JMM owes you nothing —\nno visibility, no ordering. Every concurrency bug is, at bottom, a missing edge.",[10,321,323],{"id":322},"what-volatile-guarantees-and-what-it-doesnt","What volatile guarantees — and what it doesn't",[15,325,326,328,329,332,333,336,337,340],{},[28,327,30],{}," does exactly two things: it provides ",[19,330,331],{},"visibility"," and ",[19,334,335],{},"ordering",". A volatile\nwrite is flushed so every thread sees it; a volatile read always fetches the latest value\nfrom main memory; and the write establishes a happens-before edge to every later read of\nthat field. Because of that edge, plain writes that came ",[197,338,339],{},"before"," the volatile write ride\nalong and become visible too.",[56,342,344],{"className":58,"code":343,"language":60,"meta":61,"style":61},"volatile boolean ready = false;\nint data;                 \u002F\u002F plain field\n\n\u002F\u002F writer thread\ndata = 42;                \u002F\u002F ordinary write\nready = true;             \u002F\u002F volatile write — happens-before any later read of ready\n\n\u002F\u002F reader thread\nif (ready) {              \u002F\u002F volatile read\n  use(data);              \u002F\u002F guaranteed to see 42 (carried along by happens-before)\n}\n",[28,345,346,363,374,378,383,399,414,418,423,435,447],{"__ignoreMap":61},[65,347,348,350,353,356,358,360],{"class":67,"line":68},[65,349,30],{"class":71},[65,351,352],{"class":71}," boolean",[65,354,355],{"class":79}," ready ",[65,357,92],{"class":71},[65,359,96],{"class":95},[65,361,362],{"class":79},";\n",[65,364,365,368,371],{"class":67,"line":83},[65,366,367],{"class":71},"int",[65,369,370],{"class":79}," data;                 ",[65,372,373],{"class":102},"\u002F\u002F plain field\n",[65,375,376],{"class":67,"line":106},[65,377,110],{"emptyLinePlaceholder":109},[65,379,380],{"class":67,"line":113},[65,381,382],{"class":102},"\u002F\u002F writer thread\n",[65,384,385,388,390,393,396],{"class":67,"line":125},[65,386,387],{"class":79},"data ",[65,389,92],{"class":71},[65,391,392],{"class":95}," 42",[65,394,395],{"class":79},";                ",[65,397,398],{"class":102},"\u002F\u002F ordinary write\n",[65,400,401,404,406,408,411],{"class":67,"line":149},[65,402,403],{"class":79},"ready ",[65,405,92],{"class":71},[65,407,168],{"class":95},[65,409,410],{"class":79},";             ",[65,412,413],{"class":102},"\u002F\u002F volatile write — happens-before any later read of ready\n",[65,415,416],{"class":67,"line":155},[65,417,110],{"emptyLinePlaceholder":109},[65,419,420],{"class":67,"line":177},[65,421,422],{"class":102},"\u002F\u002F reader thread\n",[65,424,426,429,432],{"class":67,"line":425},9,[65,427,428],{"class":71},"if",[65,430,431],{"class":79}," (ready) {              ",[65,433,434],{"class":102},"\u002F\u002F volatile read\n",[65,436,438,441,444],{"class":67,"line":437},10,[65,439,440],{"class":75},"  use",[65,442,443],{"class":79},"(data);              ",[65,445,446],{"class":102},"\u002F\u002F guaranteed to see 42 (carried along by happens-before)\n",[65,448,450],{"class":67,"line":449},11,[65,451,180],{"class":79},[15,453,454,455,457,458,460,461,464],{},"What ",[28,456,30],{}," does ",[19,459,244],{}," give you is ",[19,462,463],{},"atomicity",". It makes each individual read and\nwrite fresh, but it cannot make a sequence of them indivisible — which is exactly the next\ntrap.",[10,466,468],{"id":467},"atomicity-vs-visibility-vs-ordering","Atomicity vs visibility vs ordering",[15,470,471],{},"Interviewers love these three because conflating them is the classic mistake. They are\ngenuinely distinct concerns:",[473,474,475,482,488],"ul",{},[476,477,478,481],"li",{},[19,479,480],{},"Atomicity"," — an operation completes all-or-nothing; no thread sees a half-done state.",[476,483,484,487],{},[19,485,486],{},"Visibility"," — a write by one thread becomes observable to another.",[476,489,490,493,494,497],{},[19,491,492],{},"Ordering"," — the order operations ",[197,495,496],{},"appear"," to execute in.",[56,499,501],{"className":58,"code":500,"language":60,"meta":61,"style":61},"volatile int count = 0;\ncount++;   \u002F\u002F really: int tmp = count; tmp = tmp + 1; count = tmp;  — THREE steps\n",[28,502,503,520],{"__ignoreMap":61},[65,504,505,507,510,513,515,518],{"class":67,"line":68},[65,506,30],{"class":71},[65,508,509],{"class":71}," int",[65,511,512],{"class":79}," count ",[65,514,92],{"class":71},[65,516,517],{"class":95}," 0",[65,519,362],{"class":79},[65,521,522,525,528,531],{"class":67,"line":83},[65,523,524],{"class":79},"count",[65,526,527],{"class":71},"++",[65,529,530],{"class":79},";   ",[65,532,533],{"class":102},"\u002F\u002F really: int tmp = count; tmp = tmp + 1; count = tmp;  — THREE steps\n",[15,535,536,539,540,543,544,546,547,549,550,555,556,558],{},[28,537,538],{},"count++"," is a ",[19,541,542],{},"read-modify-write",": two threads can both read the same value, both add\none, both store the same result — a lost update — even though every step sees fresh memory.\n",[28,545,30],{}," covers visibility and ordering; it adds ",[19,548,318],{}," atomicity. For an atomic counter\nuse ",[19,551,552],{},[28,553,554],{},"AtomicInteger.incrementAndGet()"," (built on hardware compare-and-swap), or a lock.\nThe rule: ",[28,557,30],{}," is correct only when a write doesn't depend on the current value, like\na one-way status flag — never for read-modify-write.",[10,560,562],{"id":561},"volatile-vs-synchronized","volatile vs synchronized",[15,564,565,566,568,569,332,572,574,575,577,578,580,581,584],{},"Both establish happens-before edges and both fix visibility, but they solve different\nproblems. ",[28,567,34],{}," also gives ",[19,570,571],{},"mutual exclusion",[19,573,463],{}," over a whole\nblock; ",[28,576,30],{}," is a single-field, never-blocking visibility tool. And ",[28,579,34],{},"\nprovides visibility ",[197,582,583],{},"because"," releasing a monitor happens-before the next acquire of the\nsame monitor — it is not only a lock.",[56,586,588],{"className":58,"code":587,"language":60,"meta":61,"style":61},"volatile boolean flag;              \u002F\u002F cheap, lock-free visibility for ONE field\n\nObject lock = new Object();\nsynchronized (lock) {               \u002F\u002F exclusive access + visibility for a region\n  balance = balance - amount;       \u002F\u002F compound action made atomic\n}\n",[28,589,590,602,606,622,632,651],{"__ignoreMap":61},[65,591,592,594,596,599],{"class":67,"line":68},[65,593,30],{"class":71},[65,595,352],{"class":71},[65,597,598],{"class":79}," flag;              ",[65,600,601],{"class":102},"\u002F\u002F cheap, lock-free visibility for ONE field\n",[65,603,604],{"class":67,"line":83},[65,605,110],{"emptyLinePlaceholder":109},[65,607,608,611,613,616,619],{"class":67,"line":106},[65,609,610],{"class":79},"Object lock ",[65,612,92],{"class":71},[65,614,615],{"class":71}," new",[65,617,618],{"class":75}," Object",[65,620,621],{"class":79},"();\n",[65,623,624,626,629],{"class":67,"line":113},[65,625,34],{"class":71},[65,627,628],{"class":79}," (lock) {               ",[65,630,631],{"class":102},"\u002F\u002F exclusive access + visibility for a region\n",[65,633,634,637,639,642,645,648],{"class":67,"line":125},[65,635,636],{"class":79},"  balance ",[65,638,92],{"class":71},[65,640,641],{"class":79}," balance ",[65,643,644],{"class":71},"-",[65,646,647],{"class":79}," amount;       ",[65,649,650],{"class":102},"\u002F\u002F compound action made atomic\n",[65,652,653],{"class":67,"line":149},[65,654,180],{"class":79},[15,656,657,658,660,661,663,664,667,668,671,672,674],{},"Reach for ",[28,659,30],{}," when you only need one variable's latest value visible. Reach for\n",[28,662,34],{}," when you need an ",[19,665,666],{},"atomic compound operation"," or must keep ",[19,669,670],{},"multiple\nrelated fields"," consistent together — ",[28,673,30],{}," is per-field and cannot maintain an\ninvariant that spans two fields.",[10,676,678],{"id":677},"instruction-reordering","Instruction reordering",[15,680,681,682,685,686,689,690,693],{},"Compilers, the JIT, and CPUs may ",[19,683,684],{},"execute instructions out of program order"," as long as a\n",[197,687,688],{},"single thread's"," result is unchanged — the ",[19,691,692],{},"as-if-serial"," guarantee. Reordering powers\nregister allocation and latency hiding, so it is everywhere.",[56,695,697],{"className":58,"code":696,"language":60,"meta":61,"style":61},"int a = 1;   \u002F\u002F no dependency between these two,\nint b = 2;   \u002F\u002F so the JVM\u002FCPU may run them in either order\n\nint y = a;   \u002F\u002F depends on a — can NEVER be reordered before 'a = 1'\n",[28,698,699,716,733,737],{"__ignoreMap":61},[65,700,701,703,706,708,711,713],{"class":67,"line":68},[65,702,367],{"class":71},[65,704,705],{"class":79}," a ",[65,707,92],{"class":71},[65,709,710],{"class":95}," 1",[65,712,530],{"class":79},[65,714,715],{"class":102},"\u002F\u002F no dependency between these two,\n",[65,717,718,720,723,725,728,730],{"class":67,"line":83},[65,719,367],{"class":71},[65,721,722],{"class":79}," b ",[65,724,92],{"class":71},[65,726,727],{"class":95}," 2",[65,729,530],{"class":79},[65,731,732],{"class":102},"\u002F\u002F so the JVM\u002FCPU may run them in either order\n",[65,734,735],{"class":67,"line":106},[65,736,110],{"emptyLinePlaceholder":109},[65,738,739,741,744,746,749],{"class":67,"line":113},[65,740,367],{"class":71},[65,742,743],{"class":79}," y ",[65,745,92],{"class":71},[65,747,748],{"class":79}," a;   ",[65,750,751],{"class":102},"\u002F\u002F depends on a — can NEVER be reordered before 'a = 1'\n",[15,753,754,755,758,759,762,763,765,766,768],{},"Within one thread you never notice. Across threads you can: a second thread might observe\n",[28,756,757],{},"b"," set before ",[28,760,761],{},"a",". The JMM only forbids reordering across ",[19,764,25],{}," edges — a\nvolatile access or a lock release\u002Facquire acts as a barrier the optimizer may not cross.\nThis is the precise reason the next pattern needs ",[28,767,30],{},".",[10,770,772],{"id":771},"double-checked-locking-needs-volatile","Double-checked locking needs volatile",[15,774,775,776,778,779,782,783,786,787,789,790,793,794,797],{},"The double-checked-locking (DCL) singleton checks the instance once without a lock and once\ninside, to avoid synchronizing on every call. Without ",[28,777,30],{}," it is subtly ",[19,780,781],{},"broken",",\nbecause ",[28,784,785],{},"instance = new Singleton()"," is ",[197,788,244],{}," atomic: it allocates memory, runs the\nconstructor, and assigns the reference — and those steps may be ",[19,791,792],{},"reordered",". A second\nthread could see a non-null reference pointing at a ",[197,795,796],{},"partially constructed"," object.",[56,799,801],{"className":58,"code":800,"language":60,"meta":61,"style":61},"class Singleton {\n  private static volatile Singleton instance;   \u002F\u002F volatile is REQUIRED, not optional\n\n  static Singleton get() {\n    if (instance == null) {                \u002F\u002F 1st check, no lock — fast path\n      synchronized (Singleton.class) {\n        if (instance == null)              \u002F\u002F 2nd check, under the lock\n          instance = new Singleton();      \u002F\u002F volatile forbids the dangerous reorder\n      }\n    }\n    return instance;\n  }\n}\n",[28,802,803,812,829,833,846,866,874,891,908,913,918,926,931],{"__ignoreMap":61},[65,804,805,807,810],{"class":67,"line":68},[65,806,72],{"class":71},[65,808,809],{"class":75}," Singleton",[65,811,80],{"class":79},[65,813,814,817,820,823,826],{"class":67,"line":83},[65,815,816],{"class":71},"  private",[65,818,819],{"class":71}," static",[65,821,822],{"class":71}," volatile",[65,824,825],{"class":79}," Singleton instance;   ",[65,827,828],{"class":102},"\u002F\u002F volatile is REQUIRED, not optional\n",[65,830,831],{"class":67,"line":106},[65,832,110],{"emptyLinePlaceholder":109},[65,834,835,838,841,844],{"class":67,"line":113},[65,836,837],{"class":71},"  static",[65,839,840],{"class":79}," Singleton ",[65,842,843],{"class":75},"get",[65,845,122],{"class":79},[65,847,848,851,854,857,860,863],{"class":67,"line":125},[65,849,850],{"class":71},"    if",[65,852,853],{"class":79}," (instance ",[65,855,856],{"class":71},"==",[65,858,859],{"class":95}," null",[65,861,862],{"class":79},") {                ",[65,864,865],{"class":102},"\u002F\u002F 1st check, no lock — fast path\n",[65,867,868,871],{"class":67,"line":149},[65,869,870],{"class":71},"      synchronized",[65,872,873],{"class":79}," (Singleton.class) {\n",[65,875,876,879,881,883,885,888],{"class":67,"line":155},[65,877,878],{"class":71},"        if",[65,880,853],{"class":79},[65,882,856],{"class":71},[65,884,859],{"class":95},[65,886,887],{"class":79},")              ",[65,889,890],{"class":102},"\u002F\u002F 2nd check, under the lock\n",[65,892,893,896,898,900,902,905],{"class":67,"line":177},[65,894,895],{"class":79},"          instance ",[65,897,92],{"class":71},[65,899,615],{"class":71},[65,901,809],{"class":75},[65,903,904],{"class":79},"();      ",[65,906,907],{"class":102},"\u002F\u002F volatile forbids the dangerous reorder\n",[65,909,910],{"class":67,"line":425},[65,911,912],{"class":79},"      }\n",[65,914,915],{"class":67,"line":437},[65,916,917],{"class":79},"    }\n",[65,919,920,923],{"class":67,"line":449},[65,921,922],{"class":71},"    return",[65,924,925],{"class":79}," instance;\n",[65,927,929],{"class":67,"line":928},12,[65,930,152],{"class":79},[65,932,934],{"class":67,"line":933},13,[65,935,180],{"class":79},[15,937,938,940,941,944,945,947],{},[28,939,30],{}," bans the reordering and adds a happens-before edge, so any thread that sees the\nreference also sees a fully built object. If hand-writing DCL feels fragile, the\n",[19,942,943],{},"holder-class idiom"," (a static nested class initialized lazily by the classloader) gets\nthe same lazy, thread-safe result with no ",[28,946,30],{}," at all.",[10,949,951],{"id":950},"safe-publication","Safe publication",[15,953,954,957,958,961,962,965],{},[19,955,956],{},"Publishing"," an object means making its reference visible to other threads. ",[19,959,960],{},"Safe\npublication"," additionally guarantees that a thread which sees the reference also sees the\nobject's fully-initialized fields. Just assigning to a plain field is ",[19,963,964],{},"unsafe",": another\nthread may see the reference but stale field values inside the object.",[56,967,969],{"className":58,"code":968,"language":60,"meta":61,"style":61},"class Holder {\n  private volatile Config config;        \u002F\u002F volatile field = safe publication\n  void set(Config c) { config = c; }     \u002F\u002F readers of config see a COMPLETE Config\n  Config get() { return config; }\n}\n",[28,970,971,980,992,1017,1033],{"__ignoreMap":61},[65,972,973,975,978],{"class":67,"line":68},[65,974,72],{"class":71},[65,976,977],{"class":75}," Holder",[65,979,80],{"class":79},[65,981,982,984,986,989],{"class":67,"line":83},[65,983,816],{"class":71},[65,985,822],{"class":71},[65,987,988],{"class":79}," Config config;        ",[65,990,991],{"class":102},"\u002F\u002F volatile field = safe publication\n",[65,993,994,996,999,1002,1006,1009,1011,1014],{"class":67,"line":106},[65,995,116],{"class":71},[65,997,998],{"class":75}," set",[65,1000,1001],{"class":79},"(Config ",[65,1003,1005],{"class":1004},"s4XuR","c",[65,1007,1008],{"class":79},") { config ",[65,1010,92],{"class":71},[65,1012,1013],{"class":79}," c; }     ",[65,1015,1016],{"class":102},"\u002F\u002F readers of config see a COMPLETE Config\n",[65,1018,1019,1022,1024,1027,1030],{"class":67,"line":113},[65,1020,1021],{"class":79},"  Config ",[65,1023,843],{"class":75},[65,1025,1026],{"class":79},"() { ",[65,1028,1029],{"class":71},"return",[65,1031,1032],{"class":79}," config; }\n",[65,1034,1035],{"class":67,"line":125},[65,1036,180],{"class":79},[15,1038,1039,1040,1043,1044,1048,1049,1052,1053,1057,1058,1061,1062,1065],{},"Per ",[197,1041,1042],{},"Java Concurrency in Practice",", the safe routes are: store the reference in a\n",[19,1045,1046],{},[28,1047,30],{}," field (or ",[28,1050,1051],{},"AtomicReference","), in a ",[19,1054,1055],{},[28,1056,38],{}," field set in a constructor,\nin a field guarded by a ",[19,1059,1060],{},"lock",", or via a ",[19,1063,1064],{},"static initializer",". Transitivity is what\nmakes this work — the one volatile\u002Flock edge carries along every plain write that preceded\nthe publication.",[10,1067,1069],{"id":1068},"final-fields-and-freeze-semantics","Final fields and freeze semantics",[15,1071,1072,1074,1075,1078,1079,1082,1083,768],{},[28,1073,38],{}," fields get special ",[19,1076,1077],{},"freeze semantics",": when a constructor finishes, the JMM\n\"freezes\" the object's final fields, and any thread that obtains the object through a\nreference published ",[197,1080,1081],{},"after"," construction is guaranteed to see those fields correctly — with\n",[19,1084,1085],{},"no extra synchronization",[56,1087,1089],{"className":58,"code":1088,"language":60,"meta":61,"style":61},"class Point {\n  final int x, y;                       \u002F\u002F frozen when the constructor returns\n  Point(int x, int y) { this.x = x; this.y = y; }\n}\n\u002F\u002F Any thread that later sees a Point reference always sees the real x and y.\n",[28,1090,1091,1100,1113,1157,1161],{"__ignoreMap":61},[65,1092,1093,1095,1098],{"class":67,"line":68},[65,1094,72],{"class":71},[65,1096,1097],{"class":75}," Point",[65,1099,80],{"class":79},[65,1101,1102,1105,1107,1110],{"class":67,"line":83},[65,1103,1104],{"class":71},"  final",[65,1106,509],{"class":71},[65,1108,1109],{"class":79}," x, y;                       ",[65,1111,1112],{"class":102},"\u002F\u002F frozen when the constructor returns\n",[65,1114,1115,1118,1121,1123,1126,1128,1130,1133,1136,1139,1142,1144,1147,1149,1152,1154],{"class":67,"line":106},[65,1116,1117],{"class":75},"  Point",[65,1119,1120],{"class":79},"(",[65,1122,367],{"class":71},[65,1124,1125],{"class":1004}," x",[65,1127,31],{"class":79},[65,1129,367],{"class":71},[65,1131,1132],{"class":1004}," y",[65,1134,1135],{"class":79},") { ",[65,1137,1138],{"class":95},"this",[65,1140,1141],{"class":79},".x ",[65,1143,92],{"class":71},[65,1145,1146],{"class":79}," x; ",[65,1148,1138],{"class":95},[65,1150,1151],{"class":79},".y ",[65,1153,92],{"class":71},[65,1155,1156],{"class":79}," y; }\n",[65,1158,1159],{"class":67,"line":113},[65,1160,180],{"class":79},[65,1162,1163],{"class":67,"line":125},[65,1164,1165],{"class":102},"\u002F\u002F Any thread that later sees a Point reference always sees the real x and y.\n",[15,1167,1168,1169,1174,1175,1177,1178,1181],{},"The one caveat: the object must not ",[19,1170,1171,1172],{},"leak ",[28,1173,1138],{}," during construction (registering a\nlistener or storing ",[28,1176,1138],{}," in a shared field before the constructor returns defeats the\nfreeze). Final fields are precisely why ",[19,1179,1180],{},"immutable objects are inherently thread-safe"," and\ncan be shared freely.",[10,1183,1185],{"id":1184},"long-and-double-tearing","long and double tearing",[15,1187,1188,1189,1192,1193,1196,1197,1200,1201,1204,1205,1208,1209,1212],{},"The JMM guarantees atomic reads and writes only for ",[19,1190,1191],{},"32-bit-or-smaller"," types and\n",[19,1194,1195],{},"references",". A non-volatile ",[28,1198,1199],{},"long"," or ",[28,1202,1203],{},"double"," (64 bits) may be written as ",[19,1206,1207],{},"two separate\n32-bit stores",", so another thread can read a ",[19,1210,1211],{},"torn"," value — the high half of one write\nspliced with the low half of another.",[56,1214,1216],{"className":58,"code":1215,"language":60,"meta":61,"style":61},"long balance;                         \u002F\u002F NOT volatile — a 64-bit write may tear\n\u002F\u002F Thread A: balance = 0xFFFFFFFF_00000000L;\n\u002F\u002F Thread B may read the full value, 0, or a mangled mix of two writes.\n",[28,1217,1218,1228,1233],{"__ignoreMap":61},[65,1219,1220,1222,1225],{"class":67,"line":68},[65,1221,1199],{"class":71},[65,1223,1224],{"class":79}," balance;                         ",[65,1226,1227],{"class":102},"\u002F\u002F NOT volatile — a 64-bit write may tear\n",[65,1229,1230],{"class":67,"line":83},[65,1231,1232],{"class":102},"\u002F\u002F Thread A: balance = 0xFFFFFFFF_00000000L;\n",[65,1234,1235],{"class":67,"line":106},[65,1236,1237],{"class":102},"\u002F\u002F Thread B may read the full value, 0, or a mangled mix of two writes.\n",[15,1239,1240,1241,1245,1246,1248,1249,1251,1252,1254],{},"Declaring the field ",[19,1242,1243],{},[28,1244,30],{}," makes 64-bit reads and writes atomic (and visible). Most\nmodern 64-bit JVMs happen to write longs atomically, but the spec does not require it, so\nportable code that shares a writable ",[28,1247,1199],{},"\u002F",[28,1250,1203],{}," should mark it ",[28,1253,30],{}," or guard it\nwith a lock.",[10,1256,1258],{"id":1257},"recap","Recap",[15,1260,1261,1262,1265,1266,1268,1269,1273,1274,1277,1278,1281,1282,1284,1285,1288,1289,1293,1294,1296,1297,1300,1301,1303,1304,1307,1308,1310,1311,1314,1315,1248,1317,1319,1320,1323,1324,1326],{},"Concurrency intuition fails because the ",[19,1263,1264],{},"JMM"," lets the JVM and CPU cache, buffer, and\nreorder freely; the model's one guarantee is ",[19,1267,25],{},", and every bug is a missing\nedge. ",[19,1270,1271],{},[28,1272,30],{}," supplies ",[19,1275,1276],{},"visibility and ordering"," for a single field — perfect for a\none-way flag — but ",[19,1279,1280],{},"not atomicity",", so ",[28,1283,538],{}," still races; use ",[28,1286,1287],{},"AtomicInteger"," or a\nlock for read-modify-write, and a lock for invariants spanning multiple fields.\n",[19,1290,1291],{},[28,1292,34],{}," adds mutual exclusion and atomicity on top of visibility. Reordering is\nlegal within a thread (",[19,1295,692],{},") but constrained across threads only by\nhappens-before edges, which is why ",[19,1298,1299],{},"double-checked locking"," demands a ",[28,1302,30],{}," field.\nLean on ",[19,1305,1306],{},"safe publication"," (volatile, ",[28,1309,38],{},", locks, static init) and ",[19,1312,1313],{},"final-field\nfreeze"," for immutable objects, and remember that shared 64-bit ",[28,1316,1199],{},[28,1318,1203],{}," can ",[19,1321,1322],{},"tear","\nwithout ",[28,1325,30],{},". Master happens-before and the rest of the JMM falls into place.",[1328,1329,1330],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}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 .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);}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":61,"searchDepth":83,"depth":83,"links":1332},[1333,1334,1335,1336,1337,1338,1339,1340,1341,1342,1343,1344,1345],{"id":12,"depth":83,"text":13},{"id":42,"depth":83,"text":43},{"id":203,"depth":83,"text":204},{"id":254,"depth":83,"text":255},{"id":322,"depth":83,"text":323},{"id":467,"depth":83,"text":468},{"id":561,"depth":83,"text":562},{"id":677,"depth":83,"text":678},{"id":771,"depth":83,"text":772},{"id":950,"depth":83,"text":951},{"id":1068,"depth":83,"text":1069},{"id":1184,"depth":83,"text":1185},{"id":1257,"depth":83,"text":1258},"How the Java Memory Model really works — the visibility problem, the happens-before relationship, what volatile guarantees (and what it doesn't), double-checked locking, safe publication, final-field freeze, and long tearing.","hard","md","Java",{},"\u002Fblog\u002Fjava-volatile-memory-model","\u002Fjava\u002Fconcurrency\u002Fvolatile-memory-model",{"title":5,"description":1346},"blog\u002Fjava-volatile-memory-model","volatile & Memory Model","Concurrency","concurrency","2026-06-20","awPX8IL-rz6ssfHSXv1t6lmEKnQr7VUJzMMK398ajLM",1782244091802]