[{"data":1,"prerenderedAt":133},["ShallowReactive",2],{"qa-\u002Fjava\u002Fconcurrency\u002Fconcurrent-collections":3},{"page":4,"siblings":113,"blog":130},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"questionsCount":104,"related":105,"seo":106,"seoDescription":107,"stem":108,"subtopic":6,"topic":109,"topicSlug":110,"updated":111,"__hash__":112},"qa\u002Fjava\u002Fconcurrency\u002Fconcurrent-collections.md","Concurrent Collections",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","Java","java",{},true,4,"\u002Fjava\u002Fconcurrency\u002Fconcurrent-collections",[23,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100],{"id":24,"difficulty":25,"q":26,"a":27},"why-concurrent-collections","medium","Why use java.util.concurrent collections instead of Collections.synchronizedX?","`Collections.synchronizedMap`\u002F`synchronizedList` wrap a plain collection so that\n**every method holds one lock on the whole object**. That's correct but it\n**serializes all access** — readers block writers and each other — so it doesn't\nscale under contention. The `java.util.concurrent` collections use **finer-grained\nlocking and lock-free techniques** (CAS, lock striping, copy-on-write) so many\nthreads can proceed at once.\n\n```java\nMap\u003CString,Integer> a = Collections.synchronizedMap(new HashMap\u003C>()); \u002F\u002F one lock\nMap\u003CString,Integer> b = new ConcurrentHashMap\u003C>();                    \u002F\u002F scalable\n```\n\nThe other win: concurrent collections give **weakly consistent iterators** that\nnever throw `ConcurrentModificationException`, whereas iterating a synchronized\nwrapper still requires you to **manually synchronize on it** for the whole loop.\n**Rule of thumb:** reach for `ConcurrentHashMap`\u002F`CopyOnWriteArrayList` first; the\nsynchronized wrappers are legacy.\n",{"id":29,"difficulty":25,"q":30,"a":31},"why-not-raw-hashmap","What goes wrong if multiple threads use a plain HashMap?","A `HashMap` is **not thread-safe**, and concurrent writes can corrupt it. Beyond\nlost updates, a resize happening while another thread reads can leave the internal\nbucket structure in an inconsistent state — in older JDKs this could even spin a\nthread into an **infinite loop** (a hung CPU core). Behavior is simply\n**undefined**, which is worse than a clean exception.\n\n```java\nMap\u003CInteger,Integer> m = new HashMap\u003C>();\n\u002F\u002F two threads both doing m.put(...) concurrently:\n\u002F\u002F -> lost entries, corrupted buckets, or a CPU-pegging infinite loop\n```\n\nYou can't \"mostly get away with it\" — the failure is intermittent and\ncatastrophic. **Rule of thumb:** if a map is shared across threads, use\n`ConcurrentHashMap`; a raw `HashMap` is only safe when confined to one thread or\npublished immutably.\n",{"id":33,"difficulty":14,"q":34,"a":35},"concurrenthashmap-internals","How does ConcurrentHashMap achieve thread-safety?","Since **Java 8** it locks at the **individual bucket (bin)** level, not the whole\nmap. An empty bucket is populated with a single **CAS** (compare-and-swap, no\nlock); a non-empty bucket is updated under a `synchronized` block on that bin's\nfirst node. Because locking is per-bucket, threads touching **different buckets\nnever contend**.\n\n```java\n\u002F\u002F conceptually, per bucket:\nif (bucket == null) casBucket(node);   \u002F\u002F lock-free fast path\nelse synchronized (firstNode) { ... }  \u002F\u002F only this bin is locked\n```\n\nReads are **lock-free** — fields are `volatile`, so a `get` sees a consistent value\nwithout acquiring any lock. (Pre-Java 8 it used **segment** locking, typically 16\nstripes, which is the older \"lock striping\" answer.) **Rule of thumb:** writes\nlock one bin; reads lock nothing — that's why it scales.\n",{"id":37,"difficulty":25,"q":38,"a":39},"chm-no-null","Why does ConcurrentHashMap forbid null keys and values?","`ConcurrentHashMap` throws `NullPointerException` on a `null` key or value, unlike\n`HashMap` which allows them. The reason is **ambiguity in a concurrent setting**:\nwith `get(key)` returning `null`, you can't tell whether the key is **absent** or\n**mapped to null** — and you can't fix it with `containsKey` because another thread\nmay change the mapping between the two calls (a race).\n\n```java\nMap\u003CString,String> m = new ConcurrentHashMap\u003C>();\nm.put(\"k\", null);   \u002F\u002F NullPointerException\nm.get(\"missing\");   \u002F\u002F null is now an unambiguous \"absent\"\n```\n\nBy banning `null`, a `null` return **always** means \"not present,\" which keeps the\nlock-free read path sound. **Rule of thumb:** never store `null` in a concurrent\nmap — use a sentinel value or `Optional` if absence must be distinguished.\n",{"id":41,"difficulty":14,"q":42,"a":43},"chm-atomic-ops","What atomic operations does ConcurrentHashMap provide?","It exposes **atomic compound operations** so you don't have to lock externally for\ncheck-then-act patterns:\n\n| Method | Atomic behavior |\n| ------ | --------------- |\n| `putIfAbsent(k,v)` | put only if key absent |\n| `computeIfAbsent(k,f)` | compute & store if missing (one-time init) |\n| `computeIfPresent(k,f)` | update only if present |\n| `compute(k,f)` | recompute from current value |\n| `merge(k,v,f)` | combine new value with existing |\n\n```java\nConcurrentHashMap\u003CString,Integer> counts = new ConcurrentHashMap\u003C>();\ncounts.merge(\"hits\", 1, Integer::sum);   \u002F\u002F atomic increment-or-init\nList\u003CObject> cache = counts.computeIfAbsent(\"x\", k -> new ArrayList\u003C>()); \u002F\u002F safe lazy init\n```\n\nEach runs atomically with the bin locked, so two threads can't interleave the\nread and the write. **Rule of thumb:** prefer `merge`\u002F`compute*` over\n`get`-then-`put`, which is **not** atomic even on a concurrent map.\n",{"id":45,"difficulty":25,"q":46,"a":47},"chm-size-approximate","Why is ConcurrentHashMap.size() considered approximate?","Because the map is **never globally locked**, `size()` cannot freeze every bucket\nto count exactly. Internally it sums per-bin counters (a striped `LongAdder`-style\ntally), and entries can be added or removed **while** that sum is computed, so the\nreturned value is a **snapshot that may already be stale**.\n\n```java\nint n = map.size();        \u002F\u002F a hint, not a guarantee\n\u002F\u002F mappingCount() returns a long — preferred for very large maps\nlong exact = map.mappingCount();\n```\n\nIt's fine for metrics or sizing heuristics, but never use it for exact control\nflow (`if (size() == capacity)`) under concurrent writes. **Rule of thumb:** treat\n`size()` on any concurrent collection as an **estimate**, and prefer\n`mappingCount()` for big maps.\n",{"id":49,"difficulty":14,"q":50,"a":51},"fail-fast-vs-weakly-consistent","What is the difference between fail-fast and weakly consistent iterators?","A **fail-fast** iterator (`HashMap`, `ArrayList`) tracks a `modCount`; if the\ncollection is structurally modified during iteration it throws\n`ConcurrentModificationException` to surface the bug immediately. A **weakly\nconsistent** (or **fail-safe**) iterator from a concurrent collection **never\nthrows** it — it traverses a live or snapshot view that tolerates concurrent\nchanges.\n\n```java\nMap\u003CInteger,Integer> chm = new ConcurrentHashMap\u003C>(Map.of(1,1,2,2));\nfor (var e : chm.entrySet()) chm.put(99, 99);  \u002F\u002F 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```\n\nThe trade-off: a weakly consistent iterator **may or may not reflect** changes\nmade after it started — it gives no freshness guarantee. **Rule of thumb:**\nfail-fast = bug detector for single-threaded code; weakly consistent = safe\ntraversal under concurrency, but possibly slightly stale.\n",{"id":53,"difficulty":14,"q":54,"a":55},"copyonwritearraylist","How does CopyOnWriteArrayList work and when should you use it?","Every **mutation** (`add`, `set`, `remove`) creates a **fresh copy** of the entire\nbacking array under a lock and swaps it in. Reads and iterators work against the\n**immutable snapshot** that existed when they started, so reads are completely\n**lock-free** and iterators never throw `ConcurrentModificationException`.\n\n```java\nList\u003CListener> listeners = new CopyOnWriteArrayList\u003C>();\nlisteners.add(l);                  \u002F\u002F copies whole array\nfor (Listener x : listeners)       \u002F\u002F iterates a frozen snapshot\n    x.onEvent();                   \u002F\u002F safe even if another thread adds\n```\n\nThe cost is **O(n) per write** plus a full array allocation, so it only pays off\nwhen reads vastly outnumber writes — the classic case is a rarely-changing\n**listener\u002Fobserver list**. **Rule of thumb:** use it for read-mostly, small,\nseldom-mutated lists; never for write-heavy or large collections.\n",{"id":57,"difficulty":25,"q":58,"a":59},"copyonwritearrayset","What is CopyOnWriteArraySet?","A `Set` backed by a `CopyOnWriteArrayList`, with the **same snapshot semantics**:\nlock-free reads, copy-on-write mutations, and iterators that never throw. Because\nit stores elements in an array and checks duplicates with `equals` on add, add and\n`contains` are **O(n)** — there's no hashing.\n\n```java\nSet\u003CString> tags = new CopyOnWriteArraySet\u003C>();\ntags.add(\"a\");   \u002F\u002F scans for duplicate, then copies array\n```\n\nIt suits **small, read-mostly** sets (e.g. a handful of subscribers). For larger\nor write-heavy sets, prefer `ConcurrentHashMap.newKeySet()` (a concurrent hash\nset) which gives O(1) operations. **Rule of thumb:** `CopyOnWriteArraySet` for\ntiny read-dominated sets only.\n",{"id":61,"difficulty":25,"q":62,"a":63},"blockingqueue-overview","What is a BlockingQueue and what makes it \"blocking\"?","A `BlockingQueue` is a thread-safe queue whose **`put`** waits when the queue is\nfull and whose **`take`** waits when it's empty — the blocking handles the\ncoordination so threads don't busy-wait. It's the backbone of the\n**producer-consumer** pattern and of `ThreadPoolExecutor`'s work queue.\n\n```java\nBlockingQueue\u003CTask> q = new LinkedBlockingQueue\u003C>(100);\n\u002F\u002F producer\nq.put(task);              \u002F\u002F blocks if full\n\u002F\u002F consumer\nTask t = q.take();        \u002F\u002F blocks until an item is available\n```\n\nIt offers method families with different failure modes: throw (`add`\u002F`remove`),\nreturn special value (`offer`\u002F`poll`), block (`put`\u002F`take`), and time out\n(`offer(e,t,u)`\u002F`poll(t,u)`). **Rule of thumb:** use `put`\u002F`take` for\nproducer-consumer hand-off; the blocking does the waiting and signaling for you.\n",{"id":65,"difficulty":14,"q":66,"a":67},"blockingqueue-implementations","What are the main BlockingQueue implementations and their differences?","| Implementation | Characteristics |\n| -------------- | --------------- |\n| `ArrayBlockingQueue` | bounded, array-backed, single lock, FIFO |\n| `LinkedBlockingQueue` | optionally bounded, linked nodes, **separate put\u002Ftake locks** (higher throughput) |\n| `PriorityBlockingQueue` | unbounded, ordered by comparator (not FIFO) |\n| `DelayQueue` | elements become available only after their delay expires |\n| `SynchronousQueue` | **zero capacity** — each put hands directly to a take |\n\n```java\nBlockingQueue\u003CJob> q = new ArrayBlockingQueue\u003C>(50);  \u002F\u002F fixed back-pressure\nBlockingQueue\u003CJob> p = new PriorityBlockingQueue\u003C>(); \u002F\u002F highest-priority first\n```\n\nBounded queues provide **back-pressure** (producers block when full), which is\nusually what you want in a pipeline. **Rule of thumb:** `ArrayBlockingQueue` for a\nsimple bounded buffer, `LinkedBlockingQueue` for higher throughput,\n`PriorityBlockingQueue`\u002F`DelayQueue` when ordering or timing matters.\n",{"id":69,"difficulty":14,"q":70,"a":71},"synchronousqueue","What is a SynchronousQueue and where is it used?","A `SynchronousQueue` has **no internal capacity** — it holds zero elements. A\n`put` **blocks until another thread does a `take`** (and vice versa), so it's a\ndirect **hand-off** rather than a buffer. Each insert must rendezvous with a\nremoval.\n\n```java\nSynchronousQueue\u003CTask> q = new SynchronousQueue\u003C>();\n\u002F\u002F producer thread: q.put(task) parks until a consumer is ready\n\u002F\u002F consumer thread: q.take() picks it up directly\n```\n\nIt's the work queue used by **`Executors.newCachedThreadPool()`**: a task is\nhanded straight to an idle thread, or a new thread is spawned if none is free.\n**Rule of thumb:** use it for direct hand-off where you want a task picked up\nimmediately or not queued at all.\n",{"id":73,"difficulty":25,"q":74,"a":75},"delayqueue","What is a DelayQueue?","A `DelayQueue` holds elements implementing `Delayed`; an element can only be\n**taken once its delay has elapsed**. `take` blocks until the head element's delay\nexpires, making it ideal for **scheduled** or **expiring** work (caches, retries,\ntimeouts).\n\n```java\nclass Expiring implements Delayed {\n  long readyAt;\n  public long getDelay(TimeUnit u){ return u.convert(readyAt - now(), MILLISECONDS); }\n  public int compareTo(Delayed o){ \u002F* order by readyAt *\u002F }\n}\nDelayQueue\u003CExpiring> q = new DelayQueue\u003C>();\n```\n\nIt's internally a `PriorityQueue` ordered by remaining delay, so the soonest-ready\nitem is always at the head. **Rule of thumb:** reach for `DelayQueue` when items\nmust \"become available later,\" instead of polling with `sleep`.\n",{"id":77,"difficulty":14,"q":78,"a":79},"concurrentlinkedqueue","How is ConcurrentLinkedQueue different from a BlockingQueue?","`ConcurrentLinkedQueue` (and `ConcurrentLinkedDeque`) is an **unbounded,\nnon-blocking, lock-free** FIFO queue built on **CAS** (the Michael-Scott\nalgorithm). Its `offer`\u002F`poll` never block — `poll` simply returns `null` when the\nqueue is empty rather than waiting.\n\n```java\nQueue\u003CEvent> q = new ConcurrentLinkedQueue\u003C>();\nq.offer(e);              \u002F\u002F never blocks, never full\nEvent e = q.poll();      \u002F\u002F returns null if empty (no waiting)\n```\n\nUse it for high-throughput hand-off where consumers can **do other work** when the\nqueue is empty (poll-and-continue), not where you want a thread to **wait** for an\nitem. **Rule of thumb:** lock-free `ConcurrentLinkedQueue` for non-blocking\npipelines; a `BlockingQueue` when consumers should block on `take`.\n",{"id":81,"difficulty":14,"q":82,"a":83},"concurrentskiplistmap","What is ConcurrentSkipListMap and when would you use it?","It's a **sorted**, thread-safe map — the concurrent equivalent of `TreeMap`,\nimplementing `ConcurrentNavigableMap`. Instead of a red-black tree it uses a\n**lock-free skip list**, so it keeps keys ordered while supporting concurrent\naccess without global locking. Operations are **O(log n)**.\n\n```java\nConcurrentNavigableMap\u003CLong,Order> book = new ConcurrentSkipListMap\u003C>();\nbook.put(price, order);\nvar cheapest = book.firstEntry();        \u002F\u002F ordered access\nvar window   = book.headMap(limit);      \u002F\u002F range views, thread-safe\n```\n\nYou get the navigation methods (`firstKey`, `ceilingEntry`, `subMap`) that\n`ConcurrentHashMap` lacks. `ConcurrentSkipListSet` is the sorted-set counterpart.\n**Rule of thumb:** use it when you need **both** thread-safety and **sorted\u002Frange**\nqueries; if you only need a hash map, `ConcurrentHashMap` is faster.\n",{"id":85,"difficulty":25,"q":86,"a":87},"atomic-classes","What are the atomic classes in java.util.concurrent.atomic?","Classes like `AtomicInteger`, `AtomicLong`, `AtomicBoolean`, and\n`AtomicReference\u003CT>` provide **lock-free, thread-safe** single-variable updates\nbacked by hardware **CAS** instructions. They turn a read-modify-write into one\natomic step without `synchronized`.\n\n```java\nAtomicInteger counter = new AtomicInteger();\ncounter.incrementAndGet();          \u002F\u002F atomic ++\ncounter.addAndGet(5);               \u002F\u002F atomic += 5\n\nAtomicReference\u003CNode> head = new AtomicReference\u003C>();\nhead.compareAndSet(oldHead, newHead); \u002F\u002F atomic pointer swap\n```\n\nThey're far cheaper than a lock under moderate contention because there's no\nblocking or context switch — just a retry loop on CAS. **Rule of thumb:** for a\nsingle shared counter or flag, an atomic class beats a `synchronized` block.\n",{"id":89,"difficulty":14,"q":90,"a":91},"compare-and-set","How does compareAndSet (CAS) work?","`compareAndSet(expected, new)` atomically sets the value to `new` **only if** it\ncurrently equals `expected`, returning `true`\u002F`false`. It maps to a single CPU\ninstruction, so no lock is held. Code that wants to update based on the current\nvalue loops: read, compute, CAS, **retry if it failed** because another thread won\nthe race.\n\n```java\nAtomicInteger v = new AtomicInteger();\nint cur, next;\ndo {\n  cur  = v.get();\n  next = cur * 2;\n} while (!v.compareAndSet(cur, next));   \u002F\u002F retry on contention\n```\n\nThis is **optimistic concurrency** — assume no conflict, verify at commit. The\n`updateAndGet`\u002F`accumulateAndGet` helpers wrap that loop for you. **Rule of\nthumb:** CAS shines under low-to-moderate contention; under heavy contention the\nretry loop wastes CPU and a different structure may win.\n",{"id":93,"difficulty":14,"q":94,"a":95},"longadder","When should you use LongAdder instead of AtomicLong?","Under **high contention**, an `AtomicLong` becomes a bottleneck: every thread CASes\nthe **same** cell, so most attempts fail and retry. `LongAdder` spreads writes\nacross **multiple internal cells** (one per contending thread, roughly), so threads\nrarely collide; `sum()` adds the cells together when you need the total.\n\n```java\nLongAdder hits = new LongAdder();\nhits.increment();          \u002F\u002F updates a per-thread cell, low contention\nlong total = hits.sum();   \u002F\u002F combines all cells (a snapshot)\n```\n\nThe trade-off: it uses more memory and `sum()` is **not** a perfectly atomic\ninstant value (it's accurate when quiescent). Use it for **write-heavy counters**\n(metrics, statistics) where you read the total infrequently. **Rule of thumb:**\nmany threads incrementing -> `LongAdder`; you need the live value on every update ->\n`AtomicLong`.\n",{"id":97,"difficulty":14,"q":98,"a":99},"compound-action-atomicity","If each method is thread-safe, why can a sequence of calls still be a race?","Per-method thread-safety guarantees each **individual** call is atomic — it does\n**not** make a **sequence** of calls atomic. A classic **check-then-act** like\n\"if absent, put\" can interleave: two threads both see \"absent\" and both put.\n\n```java\n\u002F\u002F BROKEN even on a ConcurrentHashMap:\nif (!map.containsKey(k)) map.put(k, v);   \u002F\u002F two threads can both pass the check\n\n\u002F\u002F CORRECT — single atomic operation:\nmap.putIfAbsent(k, v);\n```\n\nThe fix is to use the collection's **built-in atomic compound method**\n(`putIfAbsent`, `compute`, `merge`) or, if none fits, wrap the whole sequence in\nexternal synchronization. **Rule of thumb:** thread-safe per call ≠ thread-safe per\ntransaction — make compound actions atomic explicitly.\n",{"id":101,"difficulty":25,"q":102,"a":103},"newkeyset","How do you get a concurrent Set in Java?","There's no `ConcurrentHashSet` class, so you use `ConcurrentHashMap.newKeySet()`,\nwhich returns a `Set` backed by a `ConcurrentHashMap` (values are a shared dummy\nobject). It gives **O(1)**, fully concurrent set operations with the same\nlock-free reads as the map.\n\n```java\nSet\u003CString> seen = ConcurrentHashMap.newKeySet();\nseen.add(\"a\");                 \u002F\u002F concurrent, O(1)\nboolean isNew = seen.add(\"b\"); \u002F\u002F returns false if already present\n```\n\nThis is the right choice for a large, write-heavy concurrent set;\n`CopyOnWriteArraySet` only suits tiny read-mostly cases, and\n`ConcurrentSkipListSet` is for when you need **sorted** order. **Rule of thumb:**\nfor a general-purpose thread-safe `Set`, use `ConcurrentHashMap.newKeySet()`.\n",20,null,{"description":11},"Java concurrent collections interview questions — ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue, the atomic classes, synchronized wrappers vs concurrent collections, and weakly consistent iterators.","java\u002Fconcurrency\u002Fconcurrent-collections","Concurrency","concurrency","2026-06-20","TBVG-gficrxEkHeWSKWmorxyrBLCwC-e3gy4nywN0_8",[114,118,121,125,126],{"subtopic":115,"path":116,"order":117},"Threads & Synchronization","\u002Fjava\u002Fconcurrency\u002Fthreads",1,{"subtopic":119,"path":120,"order":12},"Synchronization & Locks","\u002Fjava\u002Fconcurrency\u002Fsynchronization-locks",{"subtopic":122,"path":123,"order":124},"Executors & Thread Pools","\u002Fjava\u002Fconcurrency\u002Fexecutors-thread-pools",3,{"subtopic":6,"path":21,"order":20},{"subtopic":127,"path":128,"order":129},"volatile & Memory Model","\u002Fjava\u002Fconcurrency\u002Fvolatile-memory-model",5,{"path":131,"title":132},"\u002Fblog\u002Fjava-concurrent-collections","Java Concurrent Collections — ConcurrentHashMap, BlockingQueue & Atomics",1782244117377]