[{"data":1,"prerenderedAt":1577},["ShallowReactive",2],{"blog-\u002Fblog\u002Fjava-concurrent-collections":3},{"id":4,"title":5,"body":6,"description":1563,"difficulty":1564,"extension":1565,"framework":1566,"frameworkSlug":61,"meta":1567,"navigation":385,"order":227,"path":1568,"qaPath":1569,"seo":1570,"stem":1571,"subtopic":1572,"topic":1573,"topicSlug":1574,"updated":1575,"__hash__":1576},"blog\u002Fblog\u002Fjava-concurrent-collections.md","Java Concurrent Collections — ConcurrentHashMap, BlockingQueue & Atomics",{"type":7,"value":8,"toc":1552},"minimark",[9,14,56,135,142,146,188,267,302,306,357,502,517,536,540,568,729,744,748,769,835,858,862,919,988,991,1059,1066,1070,1131,1260,1282,1286,1331,1459,1486,1490,1548],[10,11,13],"h2",{"id":12},"why-a-separate-toolbox-for-concurrency","Why a separate toolbox for concurrency",[15,16,17,18,22,23,26,27,31,32,35,36,39,40,43,44,47,48,51,52,55],"p",{},"A ",[19,20,21],"code",{},"HashMap"," or ",[19,24,25],{},"ArrayList"," shared across threads without coordination is a latent\nbug: concurrent writes corrupt internal structure, and in older JDKs a racing\nresize could even spin a core into an ",[28,29,30],"strong",{},"infinite loop",". The historical fix was\n",[19,33,34],{},"Collections.synchronizedMap","\u002F",[19,37,38],{},"synchronizedList",", which wrap a plain collection so\n",[28,41,42],{},"every method grabs one lock on the whole object",". That's correct, but it\n",[28,45,46],{},"serializes all access"," — readers block readers, readers block writers — so it\ncollapses under contention. The ",[19,49,50],{},"java.util.concurrent"," package replaces that\nsingle coarse lock with ",[28,53,54],{},"finer-grained locking and lock-free techniques"," (CAS,\nper-bucket locks, copy-on-write, skip lists) so many threads make progress at\nonce. This guide walks the families you reach for and the judgment behind each.",[57,58,63],"pre",{"className":59,"code":60,"language":61,"meta":62,"style":62},"language-java shiki shiki-themes github-light github-dark","Map\u003CString,Integer> wrapped = Collections.synchronizedMap(new HashMap\u003C>()); \u002F\u002F one global lock\nMap\u003CString,Integer> scalable = new ConcurrentHashMap\u003C>();                    \u002F\u002F per-bucket locking\n","java","",[19,64,65,110],{"__ignoreMap":62},[66,67,70,74,78,81,84,87,90,93,97,100,103,106],"span",{"class":68,"line":69},"line",1,[66,71,73],{"class":72},"sVt8B","Map\u003C",[66,75,77],{"class":76},"szBVR","String",[66,79,80],{"class":72},",",[66,82,83],{"class":76},"Integer",[66,85,86],{"class":72},"> wrapped ",[66,88,89],{"class":76},"=",[66,91,92],{"class":72}," Collections.",[66,94,96],{"class":95},"sScJk","synchronizedMap",[66,98,99],{"class":72},"(",[66,101,102],{"class":76},"new",[66,104,105],{"class":72}," HashMap\u003C>()); ",[66,107,109],{"class":108},"sJ8bj","\u002F\u002F one global lock\n",[66,111,113,115,117,119,121,124,126,129,132],{"class":68,"line":112},2,[66,114,73],{"class":72},[66,116,77],{"class":76},[66,118,80],{"class":72},[66,120,83],{"class":76},[66,122,123],{"class":72},"> scalable ",[66,125,89],{"class":76},[66,127,128],{"class":76}," new",[66,130,131],{"class":72}," ConcurrentHashMap\u003C>();                    ",[66,133,134],{"class":108},"\u002F\u002F per-bucket locking\n",[15,136,137,138,141],{},"The other quiet win is iteration: synchronized wrappers still demand you\n",[28,139,140],{},"manually synchronize on the wrapper"," for the whole loop, while concurrent\ncollections hand you iterators that simply don't break under concurrent change.",[10,143,145],{"id":144},"concurrenthashmap-the-workhorse","ConcurrentHashMap: the workhorse",[15,147,148,151,152,155,156,159,160,163,164,167,168,171,172,175,176,179,180,183,184,187],{},[19,149,150],{},"ConcurrentHashMap"," is the default thread-safe map. Since ",[28,153,154],{},"Java 8"," it locks at\nthe ",[28,157,158],{},"individual bucket (bin)"," granularity rather than the whole map. Inserting\ninto an empty bucket is a single ",[28,161,162],{},"CAS"," (compare-and-swap) with no lock at all;\nupdating a non-empty bucket takes a ",[19,165,166],{},"synchronized"," block on that bin's first\nnode. Because the lock is per-bin, threads touching ",[28,169,170],{},"different buckets never\ncontend",". Crucially, ",[28,173,174],{},"reads are lock-free"," — the relevant fields are\n",[19,177,178],{},"volatile",", so a ",[19,181,182],{},"get"," sees a consistent value without acquiring anything. (The\npre-Java-8 design used 16 ",[28,185,186],{},"segment"," stripes — the classic \"lock striping\"\nanswer.)",[57,189,191],{"className":59,"code":190,"language":61,"meta":62,"style":62},"ConcurrentHashMap\u003CString,Integer> m = new ConcurrentHashMap\u003C>();\n\u002F\u002F conceptual write path, per bucket:\n\u002F\u002F   empty bucket  -> casBucket(node)             (lock-free fast path)\n\u002F\u002F   non-empty     -> synchronized(firstNode){...} (only this bin is locked)\nint v = m.getOrDefault(\"k\", 0);  \u002F\u002F read acquires no lock at all\n",[19,192,193,214,219,225,231],{"__ignoreMap":62},[66,194,195,198,200,202,204,207,209,211],{"class":68,"line":69},[66,196,197],{"class":72},"ConcurrentHashMap\u003C",[66,199,77],{"class":76},[66,201,80],{"class":72},[66,203,83],{"class":76},[66,205,206],{"class":72},"> m ",[66,208,89],{"class":76},[66,210,128],{"class":76},[66,212,213],{"class":72}," ConcurrentHashMap\u003C>();\n",[66,215,216],{"class":68,"line":112},[66,217,218],{"class":108},"\u002F\u002F conceptual write path, per bucket:\n",[66,220,222],{"class":68,"line":221},3,[66,223,224],{"class":108},"\u002F\u002F   empty bucket  -> casBucket(node)             (lock-free fast path)\n",[66,226,228],{"class":68,"line":227},4,[66,229,230],{"class":108},"\u002F\u002F   non-empty     -> synchronized(firstNode){...} (only this bin is locked)\n",[66,232,234,237,240,242,245,248,250,254,257,261,264],{"class":68,"line":233},5,[66,235,236],{"class":76},"int",[66,238,239],{"class":72}," v ",[66,241,89],{"class":76},[66,243,244],{"class":72}," m.",[66,246,247],{"class":95},"getOrDefault",[66,249,99],{"class":72},[66,251,253],{"class":252},"sZZnC","\"k\"",[66,255,256],{"class":72},", ",[66,258,260],{"class":259},"sj4cs","0",[66,262,263],{"class":72},");  ",[66,265,266],{"class":108},"\u002F\u002F read acquires no lock at all\n",[15,268,269,270,277,278,281,282,284,285,287,288,291,292,294,295,297,298,301],{},"One sharp edge: it ",[28,271,272,273,276],{},"forbids ",[19,274,275],{},"null"," keys and values"," and throws\n",[19,279,280],{},"NullPointerException"," if you try. In a concurrent setting a ",[19,283,275],{}," return from\n",[19,286,182],{}," would be ambiguous — absent, or mapped to null? — and you can't disambiguate\nwith ",[19,289,290],{},"containsKey"," because another thread may mutate between the two calls.\nBanning ",[19,293,275],{}," makes a ",[19,296,275],{}," return ",[28,299,300],{},"always"," mean \"absent,\" which keeps the\nlock-free read path sound.",[10,303,305],{"id":304},"atomic-operations-and-the-compound-action-trap","Atomic operations and the compound-action trap",[15,307,308,309,311,312,314,315,318,319,256,322,325,326,256,329,332,333,336,337,340,341,349,350,352,353,356],{},"The reason ",[19,310,150],{}," is more than \"a ",[19,313,21],{}," that won't crash\" is its\n",[28,316,317],{},"atomic compound operations",". ",[19,320,321],{},"putIfAbsent",[19,323,324],{},"computeIfAbsent",",\n",[19,327,328],{},"computeIfPresent",[19,330,331],{},"compute",", and ",[19,334,335],{},"merge"," each run ",[28,338,339],{},"atomically with the bin\nlocked",", so the read and the write can't be interleaved by another thread. They\nexist precisely because ",[28,342,343,344,348],{},"per-method thread-safety does not make a ",[345,346,347],"em",{},"sequence"," of\ncalls atomic"," — a ",[19,351,182],{},"-then-",[19,354,355],{},"put"," is still a race even on a concurrent map.",[57,358,360],{"className":59,"code":359,"language":61,"meta":62,"style":62},"ConcurrentHashMap\u003CString,Integer> counts = new ConcurrentHashMap\u003C>();\n\n\u002F\u002F BROKEN: two threads can both pass the check and both put\nif (!counts.containsKey(\"hits\")) counts.put(\"hits\", 0);\n\n\u002F\u002F CORRECT: each of these is a single atomic step\ncounts.merge(\"hits\", 1, Integer::sum);                 \u002F\u002F atomic increment-or-init\nvar cache = counts.computeIfAbsent(\"x\", k -> new ArrayList\u003C>()); \u002F\u002F safe lazy init\n",[19,361,362,381,387,392,429,433,439,467],{"__ignoreMap":62},[66,363,364,366,368,370,372,375,377,379],{"class":68,"line":69},[66,365,197],{"class":72},[66,367,77],{"class":76},[66,369,80],{"class":72},[66,371,83],{"class":76},[66,373,374],{"class":72},"> counts ",[66,376,89],{"class":76},[66,378,128],{"class":76},[66,380,213],{"class":72},[66,382,383],{"class":68,"line":112},[66,384,386],{"emptyLinePlaceholder":385},true,"\n",[66,388,389],{"class":68,"line":221},[66,390,391],{"class":108},"\u002F\u002F BROKEN: two threads can both pass the check and both put\n",[66,393,394,397,400,403,406,408,410,413,416,418,420,422,424,426],{"class":68,"line":227},[66,395,396],{"class":76},"if",[66,398,399],{"class":72}," (",[66,401,402],{"class":76},"!",[66,404,405],{"class":72},"counts.",[66,407,290],{"class":95},[66,409,99],{"class":72},[66,411,412],{"class":252},"\"hits\"",[66,414,415],{"class":72},")) counts.",[66,417,355],{"class":95},[66,419,99],{"class":72},[66,421,412],{"class":252},[66,423,256],{"class":72},[66,425,260],{"class":259},[66,427,428],{"class":72},");\n",[66,430,431],{"class":68,"line":233},[66,432,386],{"emptyLinePlaceholder":385},[66,434,436],{"class":68,"line":435},6,[66,437,438],{"class":108},"\u002F\u002F CORRECT: each of these is a single atomic step\n",[66,440,442,444,446,448,450,452,455,458,461,464],{"class":68,"line":441},7,[66,443,405],{"class":72},[66,445,335],{"class":95},[66,447,99],{"class":72},[66,449,412],{"class":252},[66,451,256],{"class":72},[66,453,454],{"class":259},"1",[66,456,457],{"class":72},", Integer",[66,459,460],{"class":76},"::",[66,462,463],{"class":72},"sum);                 ",[66,465,466],{"class":108},"\u002F\u002F atomic increment-or-init\n",[66,468,470,473,476,478,481,483,485,488,491,494,496,499],{"class":68,"line":469},8,[66,471,472],{"class":76},"var",[66,474,475],{"class":72}," cache ",[66,477,89],{"class":76},[66,479,480],{"class":72}," counts.",[66,482,324],{"class":95},[66,484,99],{"class":72},[66,486,487],{"class":252},"\"x\"",[66,489,490],{"class":72},", k ",[66,492,493],{"class":76},"->",[66,495,128],{"class":76},[66,497,498],{"class":72}," ArrayList\u003C>()); ",[66,500,501],{"class":108},"\u002F\u002F safe lazy init\n",[15,503,504,505,35,507,35,510,512,513,516],{},"This is the single most important habit with concurrent collections: prefer the\nbuilt-in ",[19,506,335],{},[19,508,509],{},"compute*",[19,511,321],{}," over check-then-act. Thread-safe per\ncall is ",[28,514,515],{},"not"," thread-safe per transaction — when no built-in fits, you must wrap\nthe whole sequence in external synchronization yourself.",[15,518,519,520,523,524,527,528,531,532,535],{},"One more consequence of never globally locking: ",[19,521,522],{},"size()"," is ",[28,525,526],{},"approximate",". The\nmap can't freeze every bin to count, so it sums per-bin counters while entries\ncome and go; treat the result as a hint and prefer ",[19,529,530],{},"mappingCount()"," (a ",[19,533,534],{},"long",") for\nvery large maps. Never branch control flow on it under concurrent writes.",[10,537,539],{"id":538},"fail-fast-vs-weakly-consistent-iterators","Fail-fast vs weakly consistent iterators",[15,541,542,543,22,545,547,548,551,552,555,556,559,560,563,564,567],{},"Iterating a plain ",[19,544,21],{},[19,546,25],{}," while it's structurally modified throws\n",[19,549,550],{},"ConcurrentModificationException"," — these are ",[28,553,554],{},"fail-fast"," iterators that track a\n",[19,557,558],{},"modCount"," to surface bugs immediately. Concurrent collections instead give\n",[28,561,562],{},"weakly consistent"," iterators that ",[28,565,566],{},"never throw"," it: they traverse a live or\nsnapshot view that tolerates concurrent change.",[57,569,571],{"className":59,"code":570,"language":61,"meta":62,"style":62},"Map\u003CInteger,Integer> chm = new ConcurrentHashMap\u003C>(Map.of(1,1, 2,2));\nfor (var e : chm.entrySet()) chm.put(99, 99);   \u002F\u002F fine — no exception\n\nMap\u003CInteger,Integer> hm = new HashMap\u003C>(Map.of(1,1));\nfor (var e : hm.entrySet()) hm.put(99, 99);     \u002F\u002F ConcurrentModificationException\n",[19,572,573,616,657,661,693],{"__ignoreMap":62},[66,574,575,577,579,581,583,586,588,590,593,596,598,600,602,604,606,609,611,613],{"class":68,"line":69},[66,576,73],{"class":72},[66,578,83],{"class":76},[66,580,80],{"class":72},[66,582,83],{"class":76},[66,584,585],{"class":72},"> chm ",[66,587,89],{"class":76},[66,589,128],{"class":76},[66,591,592],{"class":72}," ConcurrentHashMap\u003C>(Map.",[66,594,595],{"class":95},"of",[66,597,99],{"class":72},[66,599,454],{"class":259},[66,601,80],{"class":72},[66,603,454],{"class":259},[66,605,256],{"class":72},[66,607,608],{"class":259},"2",[66,610,80],{"class":72},[66,612,608],{"class":259},[66,614,615],{"class":72},"));\n",[66,617,618,621,623,625,628,631,634,637,640,642,644,647,649,651,654],{"class":68,"line":112},[66,619,620],{"class":76},"for",[66,622,399],{"class":72},[66,624,472],{"class":76},[66,626,627],{"class":72}," e ",[66,629,630],{"class":76},":",[66,632,633],{"class":72}," chm.",[66,635,636],{"class":95},"entrySet",[66,638,639],{"class":72},"()) chm.",[66,641,355],{"class":95},[66,643,99],{"class":72},[66,645,646],{"class":259},"99",[66,648,256],{"class":72},[66,650,646],{"class":259},[66,652,653],{"class":72},");   ",[66,655,656],{"class":108},"\u002F\u002F fine — no exception\n",[66,658,659],{"class":68,"line":221},[66,660,386],{"emptyLinePlaceholder":385},[66,662,663,665,667,669,671,674,676,678,681,683,685,687,689,691],{"class":68,"line":227},[66,664,73],{"class":72},[66,666,83],{"class":76},[66,668,80],{"class":72},[66,670,83],{"class":76},[66,672,673],{"class":72},"> hm ",[66,675,89],{"class":76},[66,677,128],{"class":76},[66,679,680],{"class":72}," HashMap\u003C>(Map.",[66,682,595],{"class":95},[66,684,99],{"class":72},[66,686,454],{"class":259},[66,688,80],{"class":72},[66,690,454],{"class":259},[66,692,615],{"class":72},[66,694,695,697,699,701,703,705,708,710,713,715,717,719,721,723,726],{"class":68,"line":233},[66,696,620],{"class":76},[66,698,399],{"class":72},[66,700,472],{"class":76},[66,702,627],{"class":72},[66,704,630],{"class":76},[66,706,707],{"class":72}," hm.",[66,709,636],{"class":95},[66,711,712],{"class":72},"()) hm.",[66,714,355],{"class":95},[66,716,99],{"class":72},[66,718,646],{"class":259},[66,720,256],{"class":72},[66,722,646],{"class":259},[66,724,725],{"class":72},");     ",[66,727,728],{"class":108},"\u002F\u002F ConcurrentModificationException\n",[15,730,731,732,735,736,739,740,743],{},"The trade-off is freshness: a weakly consistent iterator ",[28,733,734],{},"may or may not","\nreflect changes made after it started, giving no guarantee either way. So\nfail-fast is a ",[345,737,738],{},"bug detector"," for single-threaded code; weakly consistent is\n",[345,741,742],{},"safe traversal"," under concurrency that may be slightly stale.",[10,745,747],{"id":746},"copy-on-write-snapshots-for-read-mostly-data","Copy-on-write: snapshots for read-mostly data",[15,749,750,753,754,757,758,761,762,765,766,768],{},[19,751,752],{},"CopyOnWriteArrayList"," (and ",[19,755,756],{},"CopyOnWriteArraySet",") takes the opposite stance: every\n",[28,759,760],{},"mutation"," copies the entire backing array under a lock and swaps in the new\none. Reads and iterators then run against the ",[28,763,764],{},"immutable snapshot"," that existed\nwhen they began — completely lock-free, never throwing ",[19,767,550],{},".",[57,770,772],{"className":59,"code":771,"language":61,"meta":62,"style":62},"List\u003CListener> listeners = new CopyOnWriteArrayList\u003C>();\nlisteners.add(l);                  \u002F\u002F copies the whole array\nfor (Listener x : listeners)       \u002F\u002F iterates a frozen snapshot\n    x.onEvent();                   \u002F\u002F safe even if another thread mutates the list\n",[19,773,774,792,806,821],{"__ignoreMap":62},[66,775,776,779,782,785,787,789],{"class":68,"line":69},[66,777,778],{"class":72},"List\u003C",[66,780,781],{"class":76},"Listener",[66,783,784],{"class":72},"> listeners ",[66,786,89],{"class":76},[66,788,128],{"class":76},[66,790,791],{"class":72}," CopyOnWriteArrayList\u003C>();\n",[66,793,794,797,800,803],{"class":68,"line":112},[66,795,796],{"class":72},"listeners.",[66,798,799],{"class":95},"add",[66,801,802],{"class":72},"(l);                  ",[66,804,805],{"class":108},"\u002F\u002F copies the whole array\n",[66,807,808,810,813,815,818],{"class":68,"line":221},[66,809,620],{"class":76},[66,811,812],{"class":72}," (Listener x ",[66,814,630],{"class":76},[66,816,817],{"class":72}," listeners)       ",[66,819,820],{"class":108},"\u002F\u002F iterates a frozen snapshot\n",[66,822,823,826,829,832],{"class":68,"line":227},[66,824,825],{"class":72},"    x.",[66,827,828],{"class":95},"onEvent",[66,830,831],{"class":72},"();                   ",[66,833,834],{"class":108},"\u002F\u002F safe even if another thread mutates the list\n",[15,836,837,838,841,842,845,846,849,850,853,854,857],{},"The cost is ",[28,839,840],{},"O(n) plus a full allocation per write",", so this only pays off when\nreads vastly outnumber writes — the textbook case is a rarely-changing\n",[28,843,844],{},"listener\u002Fobserver list",". Use it for small, read-mostly, seldom-mutated\ncollections; never for write-heavy or large ones. For a general-purpose concurrent\n",[19,847,848],{},"Set"," (large or write-heavy), there's no ",[19,851,852],{},"ConcurrentHashSet"," class — use\n",[19,855,856],{},"ConcurrentHashMap.newKeySet()",", which gives O(1) lock-free-read set operations.",[10,859,861],{"id":860},"the-blockingqueue-family-and-producer-consumer","The BlockingQueue family and producer-consumer",[15,863,17,864,867,868,871,872,875,876,880,881,886,887,35,890,893,894,35,896,899,900,35,903,906,907,35,909,911,912,35,915,918],{},[19,865,866],{},"BlockingQueue"," is the backbone of the ",[28,869,870],{},"producer-consumer"," pattern (and of\n",[19,873,874],{},"ThreadPoolExecutor","'s work queue). Its ",[28,877,878],{},[19,879,355],{}," waits when the queue is full and\nits ",[28,882,883],{},[19,884,885],{},"take"," waits when it's empty, so the blocking itself handles the\nhand-off — no busy-waiting, no manual ",[19,888,889],{},"wait",[19,891,892],{},"notify",". It also offers method\nfamilies with different failure modes: throw (",[19,895,799],{},[19,897,898],{},"remove","), special value\n(",[19,901,902],{},"offer",[19,904,905],{},"poll","), block (",[19,908,355],{},[19,910,885],{},"), and time out (",[19,913,914],{},"offer(e,t,u)",[19,916,917],{},"poll(t,u)",").",[57,920,922],{"className":59,"code":921,"language":61,"meta":62,"style":62},"BlockingQueue\u003CTask> q = new LinkedBlockingQueue\u003C>(100);\n\u002F\u002F producer thread\nq.put(task);          \u002F\u002F blocks if the queue is full -> back-pressure\n\u002F\u002F consumer thread\nTask t = q.take();    \u002F\u002F blocks until an item is available\n",[19,923,924,947,952,965,970],{"__ignoreMap":62},[66,925,926,929,932,935,937,939,942,945],{"class":68,"line":69},[66,927,928],{"class":72},"BlockingQueue\u003C",[66,930,931],{"class":76},"Task",[66,933,934],{"class":72},"> q ",[66,936,89],{"class":76},[66,938,128],{"class":76},[66,940,941],{"class":72}," LinkedBlockingQueue\u003C>(",[66,943,944],{"class":259},"100",[66,946,428],{"class":72},[66,948,949],{"class":68,"line":112},[66,950,951],{"class":108},"\u002F\u002F producer thread\n",[66,953,954,957,959,962],{"class":68,"line":221},[66,955,956],{"class":72},"q.",[66,958,355],{"class":95},[66,960,961],{"class":72},"(task);          ",[66,963,964],{"class":108},"\u002F\u002F blocks if the queue is full -> back-pressure\n",[66,966,967],{"class":68,"line":227},[66,968,969],{"class":108},"\u002F\u002F consumer thread\n",[66,971,972,975,977,980,982,985],{"class":68,"line":233},[66,973,974],{"class":72},"Task t ",[66,976,89],{"class":76},[66,978,979],{"class":72}," q.",[66,981,885],{"class":95},[66,983,984],{"class":72},"();    ",[66,986,987],{"class":108},"\u002F\u002F blocks until an item is available\n",[15,989,990],{},"The implementations trade off differently:",[992,993,994,1003,1015,1023,1038],"ul",{},[995,996,997,1002],"li",{},[28,998,999],{},[19,1000,1001],{},"ArrayBlockingQueue"," — bounded, array-backed, single lock, FIFO; a simple\nbounded buffer.",[995,1004,1005,1010,1011,1014],{},[28,1006,1007],{},[19,1008,1009],{},"LinkedBlockingQueue"," — optionally bounded, linked nodes, ",[28,1012,1013],{},"separate\nput\u002Ftake locks"," for higher throughput.",[995,1016,1017,1022],{},[28,1018,1019],{},[19,1020,1021],{},"PriorityBlockingQueue"," — unbounded, ordered by comparator instead of FIFO.",[995,1024,1025,1030,1031,1034,1035,768],{},[28,1026,1027],{},[19,1028,1029],{},"DelayQueue"," — elements become takeable only after their ",[19,1032,1033],{},"Delayed"," expires;\nideal for scheduled\u002Fexpiring work instead of polling with ",[19,1036,1037],{},"sleep",[995,1039,1040,1045,1046,1049,1050,1052,1053,1055,1056,768],{},[28,1041,1042],{},[19,1043,1044],{},"SynchronousQueue"," — ",[28,1047,1048],{},"zero capacity","; each ",[19,1051,355],{}," blocks until a ",[19,1054,885],{},"\nrendezvous, a direct hand-off. It's the queue behind\n",[19,1057,1058],{},"Executors.newCachedThreadPool()",[15,1060,1061,1062,1065],{},"Bounded queues give you ",[28,1063,1064],{},"back-pressure"," — producers block when full — which is\nusually what you want in a pipeline so a fast producer can't exhaust memory.",[10,1067,1069],{"id":1068},"lock-free-and-sorted-alternatives","Lock-free and sorted alternatives",[15,1071,1072,1073,1076,1077,753,1080,1083,1084,1087,1088,1090,1091,1093,1094,1096,1097,1099,1100,1103,1104,1107,1108,1110,1111,1114,1115,1118,1119,1122,1123,1126,1127,1130],{},"When consumers can ",[28,1074,1075],{},"do other work"," rather than wait for an item, you don't want\nblocking at all. ",[19,1078,1079],{},"ConcurrentLinkedQueue",[19,1081,1082],{},"ConcurrentLinkedDeque",") is an\n",[28,1085,1086],{},"unbounded, non-blocking, lock-free"," FIFO built on ",[28,1089,162],{}," (the Michael-Scott\nalgorithm): ",[19,1092,902],{}," never blocks and ",[19,1095,905],{}," returns ",[19,1098,275],{}," on empty instead of\nwaiting. And when you need thread-safety ",[345,1101,1102],{},"plus"," ",[28,1105,1106],{},"sorted\u002Frange"," access — what\n",[19,1109,150],{}," can't give you — ",[19,1112,1113],{},"ConcurrentSkipListMap"," is the concurrent\n",[19,1116,1117],{},"TreeMap",", backed by a ",[28,1120,1121],{},"lock-free skip list"," with ",[28,1124,1125],{},"O(log n)"," operations and\nthe full ",[19,1128,1129],{},"NavigableMap"," API.",[57,1132,1134],{"className":59,"code":1133,"language":61,"meta":62,"style":62},"Queue\u003CEvent> events = new ConcurrentLinkedQueue\u003C>();\nevents.offer(e);                  \u002F\u002F never blocks, never \"full\"\nEvent e = events.poll();          \u002F\u002F returns null if empty (poll-and-continue)\n\nConcurrentNavigableMap\u003CLong,Order> book = new ConcurrentSkipListMap\u003C>();\nbook.put(price, order);\nvar cheapest = book.firstEntry(); \u002F\u002F ordered access, thread-safe range views\nvar window   = book.headMap(limit);\n",[19,1135,1136,1154,1167,1185,1189,1212,1222,1243],{"__ignoreMap":62},[66,1137,1138,1141,1144,1147,1149,1151],{"class":68,"line":69},[66,1139,1140],{"class":72},"Queue\u003C",[66,1142,1143],{"class":76},"Event",[66,1145,1146],{"class":72},"> events ",[66,1148,89],{"class":76},[66,1150,128],{"class":76},[66,1152,1153],{"class":72}," ConcurrentLinkedQueue\u003C>();\n",[66,1155,1156,1159,1161,1164],{"class":68,"line":112},[66,1157,1158],{"class":72},"events.",[66,1160,902],{"class":95},[66,1162,1163],{"class":72},"(e);                  ",[66,1165,1166],{"class":108},"\u002F\u002F never blocks, never \"full\"\n",[66,1168,1169,1172,1174,1177,1179,1182],{"class":68,"line":221},[66,1170,1171],{"class":72},"Event e ",[66,1173,89],{"class":76},[66,1175,1176],{"class":72}," events.",[66,1178,905],{"class":95},[66,1180,1181],{"class":72},"();          ",[66,1183,1184],{"class":108},"\u002F\u002F returns null if empty (poll-and-continue)\n",[66,1186,1187],{"class":68,"line":227},[66,1188,386],{"emptyLinePlaceholder":385},[66,1190,1191,1194,1197,1199,1202,1205,1207,1209],{"class":68,"line":233},[66,1192,1193],{"class":72},"ConcurrentNavigableMap\u003C",[66,1195,1196],{"class":76},"Long",[66,1198,80],{"class":72},[66,1200,1201],{"class":76},"Order",[66,1203,1204],{"class":72},"> book ",[66,1206,89],{"class":76},[66,1208,128],{"class":76},[66,1210,1211],{"class":72}," ConcurrentSkipListMap\u003C>();\n",[66,1213,1214,1217,1219],{"class":68,"line":435},[66,1215,1216],{"class":72},"book.",[66,1218,355],{"class":95},[66,1220,1221],{"class":72},"(price, order);\n",[66,1223,1224,1226,1229,1231,1234,1237,1240],{"class":68,"line":441},[66,1225,472],{"class":76},[66,1227,1228],{"class":72}," cheapest ",[66,1230,89],{"class":76},[66,1232,1233],{"class":72}," book.",[66,1235,1236],{"class":95},"firstEntry",[66,1238,1239],{"class":72},"(); ",[66,1241,1242],{"class":108},"\u002F\u002F ordered access, thread-safe range views\n",[66,1244,1245,1247,1250,1252,1254,1257],{"class":68,"line":469},[66,1246,472],{"class":76},[66,1248,1249],{"class":72}," window   ",[66,1251,89],{"class":76},[66,1253,1233],{"class":72},[66,1255,1256],{"class":95},"headMap",[66,1258,1259],{"class":72},"(limit);\n",[15,1261,1262,1263,1265,1266,1268,1269,1271,1272,1274,1275,1278,1279,1281],{},"Pick ",[19,1264,1079],{}," for non-blocking pipelines and a ",[19,1267,866],{}," when\nconsumers should genuinely block on ",[19,1270,885],{},". Reach for ",[19,1273,1113],{},"\n(or ",[19,1276,1277],{},"ConcurrentSkipListSet",") only when you actually need ordering — a hash-based\n",[19,1280,150],{}," is faster when you don't.",[10,1283,1285],{"id":1284},"the-atomic-package-and-cas","The atomic package and CAS",[15,1287,1288,1289,1292,1293,256,1296,325,1299,256,1302,1305,1306,1309,1310,318,1312,1315,1316,1318,1319,1322,1323,1326,1327,1330],{},"For a single shared variable, a whole collection is overkill. The\n",[19,1290,1291],{},"java.util.concurrent.atomic"," classes — ",[19,1294,1295],{},"AtomicInteger",[19,1297,1298],{},"AtomicLong",[19,1300,1301],{},"AtomicBoolean",[19,1303,1304],{},"AtomicReference\u003CT>"," — provide ",[28,1307,1308],{},"lock-free, thread-safe","\nsingle-variable updates backed by hardware ",[28,1311,162],{},[19,1313,1314],{},"compareAndSet(expected, new)","\natomically writes ",[19,1317,102],{}," only if the current value still equals ",[19,1320,1321],{},"expected","; code\nthat updates based on the current value loops and ",[28,1324,1325],{},"retries on failure"," — this is\n",[28,1328,1329],{},"optimistic concurrency",": assume no conflict, verify at commit.",[57,1332,1334],{"className":59,"code":1333,"language":61,"meta":62,"style":62},"AtomicInteger counter = new AtomicInteger();\ncounter.incrementAndGet();                 \u002F\u002F atomic ++\n\nAtomicReference\u003CNode> head = new AtomicReference\u003C>();\nint cur, next;\ndo {\n    cur  = counter.get();\n    next = cur * 2;\n} while (!counter.compareAndSet(cur, next)); \u002F\u002F retry if another thread won the race\n",[19,1335,1336,1351,1365,1369,1387,1394,1402,1416,1435],{"__ignoreMap":62},[66,1337,1338,1341,1343,1345,1348],{"class":68,"line":69},[66,1339,1340],{"class":72},"AtomicInteger counter ",[66,1342,89],{"class":76},[66,1344,128],{"class":76},[66,1346,1347],{"class":95}," AtomicInteger",[66,1349,1350],{"class":72},"();\n",[66,1352,1353,1356,1359,1362],{"class":68,"line":112},[66,1354,1355],{"class":72},"counter.",[66,1357,1358],{"class":95},"incrementAndGet",[66,1360,1361],{"class":72},"();                 ",[66,1363,1364],{"class":108},"\u002F\u002F atomic ++\n",[66,1366,1367],{"class":68,"line":221},[66,1368,386],{"emptyLinePlaceholder":385},[66,1370,1371,1374,1377,1380,1382,1384],{"class":68,"line":227},[66,1372,1373],{"class":72},"AtomicReference\u003C",[66,1375,1376],{"class":76},"Node",[66,1378,1379],{"class":72},"> head ",[66,1381,89],{"class":76},[66,1383,128],{"class":76},[66,1385,1386],{"class":72}," AtomicReference\u003C>();\n",[66,1388,1389,1391],{"class":68,"line":233},[66,1390,236],{"class":76},[66,1392,1393],{"class":72}," cur, next;\n",[66,1395,1396,1399],{"class":68,"line":435},[66,1397,1398],{"class":76},"do",[66,1400,1401],{"class":72}," {\n",[66,1403,1404,1407,1409,1412,1414],{"class":68,"line":441},[66,1405,1406],{"class":72},"    cur  ",[66,1408,89],{"class":76},[66,1410,1411],{"class":72}," counter.",[66,1413,182],{"class":95},[66,1415,1350],{"class":72},[66,1417,1418,1421,1423,1426,1429,1432],{"class":68,"line":469},[66,1419,1420],{"class":72},"    next ",[66,1422,89],{"class":76},[66,1424,1425],{"class":72}," cur ",[66,1427,1428],{"class":76},"*",[66,1430,1431],{"class":259}," 2",[66,1433,1434],{"class":72},";\n",[66,1436,1438,1441,1444,1446,1448,1450,1453,1456],{"class":68,"line":1437},9,[66,1439,1440],{"class":72},"} ",[66,1442,1443],{"class":76},"while",[66,1445,399],{"class":72},[66,1447,402],{"class":76},[66,1449,1355],{"class":72},[66,1451,1452],{"class":95},"compareAndSet",[66,1454,1455],{"class":72},"(cur, next)); ",[66,1457,1458],{"class":108},"\u002F\u002F retry if another thread won the race\n",[15,1460,1461,1462,1464,1465,1468,1469,1472,1473,1478,1479,1482,1483,1485],{},"Atomics beat ",[19,1463,166],{}," under low-to-moderate contention because there's no\nblocking or context switch — just a CAS retry. But under ",[28,1466,1467],{},"heavy"," contention every\nthread CASes the ",[28,1470,1471],{},"same"," cell, so most attempts fail and waste CPU. That's the\ncase for ",[28,1474,1475],{},[19,1476,1477],{},"LongAdder",": it spreads writes across ",[28,1480,1481],{},"multiple internal cells"," so\nthreads rarely collide, summing them only when you read the total. Use it for\nwrite-heavy counters (metrics) where you read infrequently; stick with\n",[19,1484,1298],{}," when you need the live value on every update.",[10,1487,1489],{"id":1488},"recap","Recap",[15,1491,1492,1493,1495,1496,1500,1501,35,1503,1505,1506,1508,1509,1511,1512,1515,1516,1519,1520,1523,1524,1528,1529,1533,1534,1538,1539,1542,1543,1547],{},"Concurrent collections exist because a single coarse lock doesn't scale and a raw\n",[19,1494,21],{}," is unsafe. ",[28,1497,1498],{},[19,1499,150],{}," is the default — per-bucket locking,\nlock-free reads, atomic ",[19,1502,335],{},[19,1504,509],{},", no ",[19,1507,275],{},"s, approximate ",[19,1510,522],{}," — and\nits compound methods are the cure for the ",[28,1513,1514],{},"check-then-act"," race that survives even\non a thread-safe map. ",[28,1517,1518],{},"Weakly consistent iterators"," trade freshness for never\nthrowing; ",[28,1521,1522],{},"copy-on-write"," structures trade write cost for cheap snapshot reads on\nread-mostly data. The ",[28,1525,1526],{},[19,1527,866],{}," family powers producer-consumer with\nbuilt-in back-pressure, while ",[28,1530,1531],{},[19,1532,1079],{}," (lock-free) and\n",[28,1535,1536],{},[19,1537,1113],{}," (sorted) cover non-blocking and ordered needs. Down at\nthe variable level, the ",[28,1540,1541],{},"atomic"," classes give CAS-based updates, with\n",[28,1544,1545],{},[19,1546,1477],{}," for write-heavy counters. The throughline: thread-safe per call is\nnot thread-safe per transaction — make compound actions atomic on purpose.",[1549,1550,1551],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}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 .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 .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":62,"searchDepth":112,"depth":112,"links":1553},[1554,1555,1556,1557,1558,1559,1560,1561,1562],{"id":12,"depth":112,"text":13},{"id":144,"depth":112,"text":145},{"id":304,"depth":112,"text":305},{"id":538,"depth":112,"text":539},{"id":746,"depth":112,"text":747},{"id":860,"depth":112,"text":861},{"id":1068,"depth":112,"text":1069},{"id":1284,"depth":112,"text":1285},{"id":1488,"depth":112,"text":1489},"A practical guide to Java's concurrent collections — why ConcurrentHashMap beats synchronized wrappers, weakly consistent iterators, copy-on-write structures, the BlockingQueue family, and the atomic package with CAS.","hard","md","Java",{},"\u002Fblog\u002Fjava-concurrent-collections","\u002Fjava\u002Fconcurrency\u002Fconcurrent-collections",{"title":5,"description":1563},"blog\u002Fjava-concurrent-collections","Concurrent Collections","Concurrency","concurrency","2026-06-20","qcZTBJ4kozAOfDwP4WCex_gS1H_HdX5luBdmcGiOItY",1782244090532]