Skip to content
  • ConcurrentHashMap, Atomics & Locks · Java

ConcurrentHashMap, Atomics & Locks

LearnViaNotes @learnvianotes· 6 min read

java.util.concurrent replaces most hand-written synchronized/wait code with tested building blocks: concurrent collections, atomic variables, explicit locks and synchronizers.

Thread-safe maps compared

HashtableCollections.synchronizedMapConcurrentHashMap
LockingEvery method locks the whole tableEvery method locks one mutexCAS + per-bin locks; reads are lock-free
Concurrent reads/writesNoNoYes
null keys/valuesNot allowedAllowed (if the backing map allows)Not allowed
Iteration while modifyingFail-fastMust lock manually; else CMEWeakly consistent, never throws CME
Atomic compound opsNoNomerge, compute, putIfAbsent…
StatusLegacySimple wrapperDefault choice
Java
import java.util.List;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ConcurrentHashMap<String, Integer> counts = new ConcurrentHashMap<>();
        List<String> words = List.of("a", "b", "a", "c", "a", "b");

        Runnable countAll = () -> { for (String w : words) counts.merge(w, 1, Integer::sum); };  // atomic
        Thread[] ts = new Thread[4];
        for (int i = 0; i < ts.length; i++) (ts[i] = new Thread(countAll)).start();
        for (Thread t : ts) t.join();

        System.out.println(new TreeMap<>(counts));     // sorted for display
        counts.computeIfAbsent("d", k -> 0);
        counts.compute("d", (k, v) -> v + 100);
        System.out.println("d = " + counts.get("d") + ", size = " + counts.size());
    }
}
Output
{a=12, b=8, c=4}
d = 100, size = 4

Atomic variables and CAS

Java
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.LongAdder;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        AtomicInteger a = new AtomicInteger(5);
        System.out.println(a.compareAndSet(5, 10) + " " + a.get());   // expected 5: swap
        System.out.println(a.compareAndSet(5, 20) + " " + a.get());   // stale expectation: no swap
        System.out.println(a.updateAndGet(x -> x * 3));
        System.out.println(a.getAndIncrement() + " then " + a.get());

        AtomicInteger hits = new AtomicInteger();
        LongAdder adder = new LongAdder();
        Runnable work = () -> {
            for (int i = 0; i < 10_000; i++) { hits.incrementAndGet(); adder.increment(); }
        };
        Thread[] ts = new Thread[4];
        for (int i = 0; i < ts.length; i++) (ts[i] = new Thread(work)).start();
        for (Thread t : ts) t.join();
        System.out.println("AtomicInteger = " + hits.get() + ", LongAdder = " + adder.sum());
    }
}
Output
true 10
false 10
30
30 then 31
AtomicInteger = 40000, LongAdder = 40000
ClassUse
AtomicInteger / AtomicLongCounters, sequence numbers, compareAndSet state machines
AtomicBooleanRun-once flags (if (started.compareAndSet(false, true)))
AtomicReference<V>Swap an immutable object atomically
LongAdderHot counters under heavy contention — cells per thread, summed on read
AtomicIntegerArrayPer-index atomic updates

ReentrantLock and Condition

An explicit Lock does what synchronized does plus: try without blocking, time out, be interrupted while waiting, optional fairness, and several wait-sets (Conditions) per lock.

Java
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        ReentrantLock lock = new ReentrantLock();
        lock.lock();                                   // main holds it
        try {
            Thread t = new Thread(() -> System.out.println("worker tryLock: " + lock.tryLock()));
            t.start();
            t.join();
        } finally {
            lock.unlock();                             // ALWAYS unlock in finally
        }
        Thread t2 = new Thread(() -> {
            if (lock.tryLock()) {
                try { System.out.println("worker tryLock after unlock: true"); }
                finally { lock.unlock(); }
            }
        });
        t2.start();
        t2.join();
        System.out.println("held now? " + lock.isLocked());
    }
}
Output
worker tryLock: false
worker tryLock after unlock: true
held now? false
Java — Condition (compile-checked)
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

class LockBuffer<T> {
    private final Deque<T> items = new ArrayDeque<>();
    private final int capacity;
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();    // two separate wait-sets
    private final Condition notEmpty = lock.newCondition();

    LockBuffer(int capacity) { this.capacity = capacity; }

    void put(T x) throws InterruptedException {
        lock.lock();
        try {
            while (items.size() == capacity) notFull.await();
            items.addLast(x);
            notEmpty.signal();              // wake only a consumer
        } finally {
            lock.unlock();
        }
    }

    T take() throws InterruptedException {
        lock.lock();
        try {
            while (items.isEmpty()) notEmpty.await();
            T x = items.removeFirst();
            notFull.signal();               // wake only a producer
            return x;
        } finally {
            lock.unlock();
        }
    }
}
synchronizedReentrantLock
ReleaseAutomatic at block endManual unlock() in finally
Try / timeoutNotryLock(), tryLock(t, unit)
Interruptible wait for lockNolockInterruptibly()
FairnessNonew ReentrantLock(true) (FIFO-ish, slower)
ConditionsOne wait-set (wait/notify)Many (newCondition())
ReentrantYesYes

ReentrantReadWriteLock lets many readers in at once but writers alone — good for read-mostly data. StampedLock (Java 8) adds optimistic reads for even cheaper read paths.

Synchronizers

ToolWhat it doesReusable?Typical use
CountDownLatch(n)await() blocks until countDown() was called n timesNoWait for N services to start / N tasks to finish
CyclicBarrier(n, action)n threads wait for each other, then all continueYesPhased parallel algorithms
Semaphore(permits)acquire()/release() permitsYesLimit concurrency (max 10 DB connections)
PhaserFlexible barrier with dynamic partiesYesMulti-phase work with join/leave
ExchangerTwo threads swap objectsYesPipeline buffers
Java
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        // CountDownLatch: wait for 3 workers
        CountDownLatch done = new CountDownLatch(3);
        List<String> results = new CopyOnWriteArrayList<>();
        for (String svc : List.of("db", "cache", "queue")) {
            new Thread(() -> { results.add(svc + " ready"); done.countDown(); }).start();
        }
        done.await();
        System.out.println(results.stream().sorted().toList());

        // Semaphore: at most 2 tasks inside at once
        Semaphore permits = new Semaphore(2);
        AtomicInteger inside = new AtomicInteger(), maxInside = new AtomicInteger();
        try (ExecutorService pool = Executors.newFixedThreadPool(6)) {
            for (int i = 0; i < 6; i++) {
                pool.submit(() -> {
                    permits.acquire();
                    try {
                        maxInside.accumulateAndGet(inside.incrementAndGet(), Math::max);
                        Thread.sleep(20);
                        inside.decrementAndGet();
                    } finally {
                        permits.release();
                    }
                    return null;
                });
            }
        }
        System.out.println("max concurrent <= 2: " + (maxInside.get() <= 2));
    }
}
Output
[cache ready, db ready, queue ready]
max concurrent <= 2: true

How does ConcurrentHashMap achieve thread safety (Java 8+)?

Why doesn't ConcurrentHashMap allow null?

AtomicInteger vs LongAdder?

CountDownLatch vs CyclicBarrier?

Free for students and teachers

Write notes like this one

Cards for every idea, quizzes and flashcards, read aloud — on the web and your phone.

Something wrong with this note? Open it to report