Fundamentals 38 min read

Master Java Concurrency: HashMap, ConcurrentHashMap, JMM, Thread Pools & More

Explore deep Java concurrency concepts, from HashMap internals and its JDK7/JDK8 differences to ConcurrentHashMap’s lock strategies, thread states, memory models, volatile, CAS, synchronized, thread pools, AQS, and common interview questions, providing comprehensive insights for mastering multithreaded programming.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
Master Java Concurrency: HashMap, ConcurrentHashMap, JMM, Thread Pools & More

1. HashMap

HashMap is a frequently asked interview question that tests a Java developer's fundamentals. Its key characteristics are:

HashMap does not guarantee order of entries. Both keys and values may be null. It is not thread‑safe; use Hashtable in multithreaded scenarios. In JDK8 the underlying structure is array + linked list + red‑black tree; in JDK7 it is array + linked list. The initial capacity and load factor are critical for performance and should rarely be changed. HashMap is lazily created – the internal table is built only when the first put occurs. When a bucket’s linked list is converted to a red‑black tree it first becomes a doubly linked list, then a tree; the tree and the list coexist. During put, two keys are compared to decide left or right placement in the tree. When deleting, the implementation checks whether the red‑black tree needs to be converted back to a list. The root node and the table entry are merged via the MoveRootToFront operation.

Common interview points for HashMap include its internal data structure, put/get/remove processes, hash function, resizing, important parameters, thread‑safety, differences between JDK7 and JDK8, and the linked‑list‑to‑tree conversion.

2. ConcurrentHashMap

ConcurrentHashMap is a concurrent container widely used in multithreaded environments. Its implementation differs significantly between JDK7 and JDK8.

2.1 JDK7

In JDK7 ConcurrentHashMap uses a segment + HashEntry design with segment‑level locks. The number of segments determines the maximum concurrency; the segment array cannot be resized, only the HashEntry objects can grow.

The put process roughly follows the diagram below:

ConcurrentHashMap allows multiple modifications to proceed concurrently by using lock‑splitting. Each segment is essentially a small HashTable protected by a ReentrantLock.

2.2 JDK8

In JDK8 the segment lock is removed. Concurrency is achieved with CAS + synchronized, and the bucket entries are now Node objects that can form a red‑black tree when a bucket becomes too large.

Read operations in JDK8 do not use any synchronization, so they are fully concurrent. Write operations follow the same logic as HashMap but add CAS and synchronized to ensure atomicity and handle resizing. The lock is now fine‑grained to the individual table slot.

The get method reads without locking, relying on the volatile nature of the value and next fields to guarantee visibility.

3. Concurrency Basics

The purpose of concurrent programming is to make full use of CPU resources. Multithreading is not always faster than single‑threaded execution; the actual benefit depends on the task.

3.1 Process vs Thread

A process is the smallest unit the operating system schedules and allocates resources to. A thread is a smaller execution unit within a process, sharing the process’s memory space.

3.2 Concurrency vs Parallelism

Concurrency : multiple threads operate on the same resource, with rapid context switches on a single‑core CPU. Parallelism : multiple CPUs execute tasks simultaneously.

3.3 Thread States

Java defines six thread states: New, Runnable, Running, Blocked, Waiting/Timed_Waiting, and Terminated.

3.4 Blocked vs Waiting

Blocked occurs when a thread attempts to acquire a synchronized lock that is held by another thread. Waiting occurs when a thread calls Object.wait() and releases the monitor, waiting for a notification.

3.5 yield vs sleep

Both pause the current thread without releasing the lock. sleep specifies a time duration; yield yields the processor’s time slice and may resume immediately.

3.6 wait vs sleep

wait is defined in Object, releases the monitor, and must be called inside a synchronized block. sleep is defined in Thread, does not release the monitor, and throws InterruptedException.

3.7 Thread Creation Methods

Extend Thread and override run().

Implement Runnable and pass it to a Thread.

Implement Callable, wrap with FutureTask, and pass to a Thread.

Use a thread pool.

Use Spring’s @Async annotation.

3.8 Deadlock

A deadlock occurs when two or more threads hold resources the other needs, and none can proceed.

Four necessary conditions: mutual exclusion, hold‑and‑wait, no preemption, and circular wait.

4. Java Memory Model (JMM)

4.1 Origin

CPU, memory, and disk speed differences lead to caches (L1/L2/L3) and consequently to cache‑coherency and memory‑visibility problems, as well as instruction reordering.

Reordering can be performed by the compiler, the processor, or the memory system.

These issues affect atomicity, visibility, and ordering, which are addressed by the JMM.

4.2 Memory Barriers

Memory barriers are CPU instructions that prevent certain reorderings and ensure visibility. Volatile variables use memory barriers internally.

4.3 happens‑before

The happens‑before relation guarantees that the result of one action is visible to another. It is enforced by program order, monitor unlock/lock, volatile write/read, thread start/join, and other rules.

4.4 as‑if‑serial

Even with reordering, a single‑threaded program must appear to execute statements in program order.

5. volatile

volatile guarantees visibility but not atomicity. Writes flush the local cache to main memory; reads invalidate the local cache and fetch the latest value.

Four types of memory barriers are used: StoreStore, StoreLoad, LoadLoad, and LoadStore.

6. Singleton (DCL + volatile)

6.1 Standard implementation

public class SingleDcl {
    private volatile static SingleDcl instance;
    private SingleDcl() {}
    public static SingleDcl getInstance() {
        if (instance == null) {
            synchronized (SingleDcl.class) {
                if (instance == null) {
                    instance = new SingleDcl();
                }
            }
        }
        return instance;
    }
}

volatile prevents instruction reordering that could expose a partially constructed object.

6.2 Why volatile

Without volatile, the write of the instance reference could be reordered before the constructor finishes, allowing another thread to see a half‑initialized object.

7. Thread Pool

7.1 Five‑minute overview

A thread pool has seven key parameters:

corePoolSize – number of core threads (always kept alive).

maximumPoolSize – maximum number of threads.

keepAliveTime – idle time before excess threads are terminated.

TimeUnit – unit for keepAliveTime.

BlockingQueue – task queue.

threadFactory – creates new threads.

RejectedExecutionHandler – policy when the queue is full and the pool cannot grow.

7.2 Correct creation

Using Executors factories can cause OOM because LinkedBlockingQueue may be unbounded. Create a ThreadPoolExecutor directly and specify a bounded queue.

private static ExecutorService executor = new ThreadPoolExecutor(
        10, 10, 60L, TimeUnit.SECONDS,
        new ArrayBlockingQueue<>(10));

7.3 Common pools

Executors.newFixedThreadPool

Executors.newSingleThreadExecutor

Executors.newCachedThreadPool

Executors.newScheduledThreadPool

ThreadPoolExecutor

7.4 Core points

Understanding corePoolSize, maximumPoolSize, queue type, rejection policies, and how to size a pool for I/O‑bound vs CPU‑bound workloads is essential for interview questions.

8. ThreadLocal

ThreadLocal provides a variable that is local to each thread. Internally each Thread holds a ThreadLocalMap where the key is a weak reference to the ThreadLocal object, allowing the map entry to be reclaimed when the ThreadLocal is no longer referenced.

When get() or set() is called, the map periodically removes stale entries, preventing memory leaks of the stored values.

9. CAS

Compare‑And‑Swap is an atomic instruction that updates a variable only if it currently holds an expected value. It suffers from the ABA problem, high CPU cost under contention, and is limited to single‑variable updates.

10. synchronized

10.1 Overview

synchronized can protect instance methods, static methods, or code blocks, guaranteeing mutual exclusion, visibility, and ordering (but not preventing reordering).

10.2 Implementation

In JDK6 and later the lock can be in four states: no‑lock, biased, lightweight, and heavyweight. The lock upgrades as contention increases and never downgrades, except that a biased lock can be revoked.

10.3 Lock upgrade table

Lock State

Advantage

Disadvantage

Suitable Scenario

Biased

Zero overhead when there is no contention.

Revocation cost when contention appears.

Almost no other thread competes.

Lightweight

Spins instead of blocking, fast response.

CPU waste if it spins too long.

Few threads, short lock hold time.

Heavyweight

Threads block, no CPU spin.

Longer latency.

Many contending threads, long lock hold.

10.4 Ordering

synchronized establishes a monitorenter/monitorexit pair, which creates a happens‑before edge, ensuring that actions within the synchronized block appear in program order to other threads.

10.5 wait vs notify

wait releases the monitor and places the thread in the wait set; notify/notifyAll moves waiting threads back to the lock pool, but they cannot run until the monitor is released.

11. AQS

11.1 Thread alternation example

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main {
    private static Lock lock = new ReentrantLock();
    private static Condition c1 = lock.newCondition();
    private static Condition c2 = lock.newCondition();
    private static CountDownLatch start = new CountDownLatch(1);
    public static void main(String[] args) {
        char[] letters = "ABCDEFGHI".toCharArray();
        char[] numbers = "123456789".toCharArray();
        Thread t1 = new Thread(() -> {
            try {
                lock.lock();
                start.countDown();
                for (char ch : letters) {
                    c1.signal();
                    System.out.print(ch);
                    c2.await();
                }
                c1.signal();
            } catch (InterruptedException e) { }
            finally { lock.unlock(); }
        });
        Thread t2 = new Thread(() -> {
            try {
                start.await();
                lock.lock();
                for (char ch : numbers) {
                    c2.signal();
                    System.out.print(ch);
                    c1.await();
                }
                c2.signal();
            } catch (InterruptedException e) { }
            finally { lock.unlock(); }
        });
        t1.start();
        t2.start();
    }
}

11.2 AQS internals

AQS uses a volatile state field updated with CAS. Threads that fail to acquire the state are enqueued in a FIFO sync queue. Conditions have a separate wait queue; await() moves the thread to the condition queue, and signal() transfers it back to the sync queue.

12. Threading Tips

Prefer stack‑confined variables to avoid sharing.

Prevent thread starvation by ensuring fair resource allocation and limiting lock hold time.

Follow a systematic performance tuning process: correctness first, then profiling, then apply Amdahl’s law.

Use read‑write locks for read‑heavy workloads, CAS for simple atomic updates, and JDK‑provided concurrent collections whenever possible.

13. End

Typical interview questions include differences between synchronized and ReentrantLock, lock state upgrades, CAS drawbacks, volatile implementation, object memory layout, double‑checked locking, as‑if‑serial vs happens‑before, ThreadLocal memory‑leak mitigation, and thread‑pool configuration.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

ThreadPoolHashMapvolatileJMM
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.