Java Collections + Concurrency
What is the difference between HashMap and ConcurrentHashMap?
HashMap is not thread-safe. ConcurrentHashMap is designed for safe concurrent reads and updates without locking the entire map.
The Short Answer
HashMap is fast and commonly used, but it is not safe when multiple threads modify it at the same time.
ConcurrentHashMap is designed for concurrent access. It lets multiple threads safely read and update the map with much better scalability than locking one entire HashMap.
The Real Problem
Imagine 100 threads updating a leaderboard, cache, request counter, or session map at the same time.
With a plain HashMap, two threads can interfere with each other. One thread may overwrite another thread's update, or one thread may observe the map while another thread is changing its internal structure.
Lost Update / Overwrite
Both threads read the same old value and write back the same new value. One update disappears.
Reading During Mutation
A map is not just key-value pairs. It has internal structure. If one thread reads while another changes that structure, the reader may observe an unsafe intermediate state.
Why Not Just Lock the Whole HashMap?
You could do this:
Map<String, Integer> scores = Collections.synchronizedMap(new HashMap<>());This can make access safer, but it usually means one major lock controls access to the map. If many threads are using the map, they wait on that same lock.
That may be correct, but it can become slow. It is like having 100 cashiers but forcing every customer to use one checkout line.
The Mental Model
Synchronized HashMap
One Big Lock
Many operations may compete for the same lock, even if they are touching unrelated keys.
ConcurrentHashMap
More Fine-Grained Concurrency
ConcurrentHashMap does not lock the entire map for every operation. It allows more parallelism.
Important: Thread-Safe Does Not Mean Every Pattern Is Safe
ConcurrentHashMap makes individual operations safe, but you still need to be careful with multi-step logic.
This pattern is risky:
if (!map.containsKey(userId)) {
map.put(userId, new UserSession());
}Prefer atomic methods like:
map.computeIfAbsent(userId, id -> new UserSession());Simple Counter Example
ConcurrentHashMap<String, LongAdder> requestCounts =
new ConcurrentHashMap<>();
public void recordRequest(String endpoint) {
requestCounts
.computeIfAbsent(endpoint, key -> new LongAdder())
.increment();
}Common Interview Follow-Ups
Does ConcurrentHashMap lock the whole map?
No. That is one of the main reasons it scales better than synchronizing an entire HashMap.
Can race conditions still happen?
Yes, if your business logic uses unsafe multi-step patterns.