Chaturmind
LearnDSASystem DesignBlogPremium
Sign inGet started
Chaturmind

Structured learning paths for engineers who want to go deep. Written by practitioners.

Learn

  • Java
  • DSA
  • System Design
  • Spring Boot
  • AI / ML

Company

  • Blog
  • Premium
  • Contact

Legal

  • Privacy Policy
  • Terms of Service

© 2026 Chaturmind. All rights reserved.

Built for engineers who want to go deep.


← Java Concurrency & Multithreading

Threads & Runnable

  • Introduction to Threads
  • ExecutorService & Thread Pools

Synchronization & Memory Model

  • synchronized and Locks
  • volatile and the Memory Model
  • CompletableFuture

Concurrent Collections

  • ConcurrentHashMap & CopyOnWriteArrayList
Chaturmind
← Java Concurrency & Multithreading

Threads & Runnable

  • Introduction to Threads
  • ExecutorService & Thread Pools

Synchronization & Memory Model

  • synchronized and Locks
  • volatile and the Memory Model
  • CompletableFuture

Concurrent Collections

  • ConcurrentHashMap & CopyOnWriteArrayList
HomeLearnJavaJava ConcurrencyConcurrent Data Structures
✓ FreeIntermediate· 13 min read

Concurrent Collections

Use ConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue and other thread-safe collections.

Published February 15, 2025


Concurrent Collections

The java.util.concurrent package provides thread-safe collection implementations that outperform manually synchronized collections by using fine-grained locking or lock-free algorithms.

ConcurrentHashMap

The go-to thread-safe map. Uses segment-based locking (Java 7) or CAS + synchronized per bucket (Java 8+), allowing concurrent reads and fine-grained writes.

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

// All operations are thread-safe
map.put("a", 1);
map.get("a");         // never returns stale data
map.remove("a");

// Atomic compound operations
map.putIfAbsent("b", 2);           // put only if key absent
map.computeIfAbsent("c", k -> k.length()); // compute if absent
map.computeIfPresent("c", (k, v) -> v + 1); // update if present
map.compute("d", (k, v) -> v == null ? 1 : v + 1); // always compute
map.merge("e", 1, Integer::sum);   // merge with existing value

// ConcurrentHashMap does NOT allow null keys or null values
map.put(null, 1);  // throws NullPointerException!

CopyOnWriteArrayList

Thread-safe list that creates a fresh copy of the array on every write. Perfect for read-heavy, write-rare scenarios (event listener lists, configuration).

CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();

// Iteration is safe even while another thread is adding/removing
for (String item : list) {
    // snapshot iteration — won't throw ConcurrentModificationException
    process(item);
}

list.add("item");    // creates a new copy of the internal array

// Warning: very expensive for write-heavy workloads!

BlockingQueue

A thread-safe queue that blocks on take() (if empty) and put() (if full). Perfect for producer-consumer patterns.

BlockingQueue<Task> queue = new LinkedBlockingQueue<>(100); // bounded

// Producer
new Thread(() -> {
    while (true) {
        queue.put(generateTask()); // blocks if queue full
    }
}).start();

// Consumer
new Thread(() -> {
    while (true) {
        Task task = queue.take(); // blocks if queue empty
        process(task);
    }
}).start();

BlockingQueue implementations:

  • LinkedBlockingQueue — optionally bounded, linked nodes
  • ArrayBlockingQueue — fixed-capacity, array-backed, FIFO
  • PriorityBlockingQueue — unbounded, ordered by priority
  • SynchronousQueue — zero-capacity; each put blocks until a take
  • DelayQueue — elements become available only after a delay

ConcurrentLinkedQueue / Deque

// Non-blocking, lock-free FIFO queue (uses CAS)
Queue<String> queue = new ConcurrentLinkedQueue<>();
queue.offer("item");    // non-blocking add
queue.poll();           // non-blocking remove (returns null if empty)
queue.peek();           // non-blocking peek

Collections.synchronizedXxx — avoid these

// These synchronize every method but not iteration
List<String> syncList = Collections.synchronizedList(new ArrayList<>());

// UNSAFE: iteration is not synchronized!
for (String s : syncList) { ... } // must wrap in synchronized(syncList) {}

// Prefer CopyOnWriteArrayList or ConcurrentHashMap instead

Atomic Classes

AtomicInteger counter = new AtomicInteger(0);
counter.incrementAndGet();            // atomic ++
counter.getAndAdd(5);                 // atomic +=5
counter.compareAndSet(5, 10);        // CAS: if current==5, set to 10

AtomicReference<String> ref = new AtomicReference<>("initial");
ref.compareAndSet("initial", "updated"); // atomic reference swap

AtomicLong longCounter = new AtomicLong();
LongAdder adder = new LongAdder(); // better for high-contention counting

Interview Tips

  1. ConcurrentHashMap vs Hashtable: CHM uses fine-grained locks; Hashtable synchronizes every method with a single lock — CHM is dramatically faster.
  2. ConcurrentHashMap vs Collections.synchronizedMap: same issue — synchronized map holds one lock for all operations.
  3. Know when to prefer LongAdder over AtomicLong: under high contention, LongAdder distributes counting across multiple cells, avoiding CAS retry loops.

Previous

CompletableFuture

AI Tutor

Lesson: Concurrent Collections

Quick actions

AI responses can be inaccurate. Verify critical information.