Command Palette

Search for a command to run...

Lesson 41.3 · Concurrency-Aware Data Structures

Concurrent Collections and Executors

ConcurrentHashMap gives thread-safe maps with atomic merge/compute. ExecutorService runs tasks on a pool of threads and returns Futures. Reach for these before writing locks yourself.

12 min

Think of it like this

A kitchen with several chefs (a thread pool) taking order tickets from one rail (the task queue), all writing tallies on a whiteboard designed so two chefs can write at once without smudging each other (a concurrent map).

1.The standard toolkit

ConcurrentHashMap: reads don't block, writes lock only a small part of the table, and merge, compute and putIfAbsent are atomic. Check-then-act sequences (if (!map.containsKey(k)) map.put(k, v)) are still races; use the atomic methods instead.

ExecutorService: Executors.newFixedThreadPool(n) reuses n threads; submit returns a Future whose get() waits for the result. Always shutdown() when done. Since Java 21, Executors.newVirtualThreadPerTaskExecutor() creates cheap virtual threads for I/O-heavy work.

Other useful classes: CopyOnWriteArrayList (rare writes, many reads), ConcurrentLinkedQueue (lock-free queue), LongAdder (high-contention counters), and ConcurrentSkipListMap (a sorted concurrent map).

Main.java
import java.util.TreeMap;
import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) throws Exception {
        ConcurrentHashMap<String, Integer> counts = new ConcurrentHashMap<>();
        ExecutorService pool = Executors.newFixedThreadPool(4);
        for (int t = 0; t < 4; t++)
            pool.submit(() -> {
                for (int i = 0; i < 1000; i++) {
                    counts.merge("a", 1, Integer::sum);                       // atomic read-modify-write
                    counts.merge(i % 2 == 0 ? "even" : "odd", 1, Integer::sum);
                }
            });
        pool.shutdown();
        pool.awaitTermination(10, TimeUnit.SECONDS);
        System.out.println(new TreeMap<>(counts));
    }
}

Output

{a=4000, even=2000, odd=2000}

Remember

  • Use atomic map methods, not check-then-act.
  • Pools reuse threads; shut them down.
  • Virtual threads for blocking I/O (Java 21+).

Common mistakes

  • Wrapping a HashMap in synchronized blocks everywhere instead of using ConcurrentHashMap.
  • Forgetting shutdown(), which keeps the JVM alive.