Given that HashMaps in jdk1.6 and above cause problems with multithreading, how should I fix my code
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
The fix is not to look for a "safe" HashMap version in a different JDK release. The real issue is that HashMap is not thread-safe, so if multiple threads read and write it concurrently, you need a concurrent collection or explicit synchronization.
What Is Actually Wrong with HashMap
The Java API documentation is explicit: HashMap is unsynchronized. If multiple threads access it and at least one thread modifies it structurally, access must be synchronized externally.
That means the problem is not unique to JDK 1.6 or above. Old blog posts often refer to resize and rehash bugs that became very visible under concurrent misuse, but the durable rule is simpler:
- '
HashMapis fine for single-threaded use' - '
HashMapis not fine for concurrent mutation without synchronization'
A race can show up as lost updates, stale reads, corrupted internal state, or intermittent exceptions. The failure mode depends on timing, which is exactly why these bugs are painful to debug.
The First Fix: Use ConcurrentHashMap
For most shared mutable maps, ConcurrentHashMap is the right replacement.
This solves two problems at once:
- the map itself is designed for concurrent access
- the update uses
merge, which makes the read-modify-write operation atomic
That second point matters. Replacing HashMap with ConcurrentHashMap but keeping non-atomic update logic is still buggy.
Why Simple get Plus put Is Not Enough
This code is incorrect even with ConcurrentHashMap:
Two threads can still interleave and overwrite each other. Use atomic APIs such as:
- '
putIfAbsent' - '
compute' - '
computeIfAbsent' - '
merge'
Here is the safe version again:
When Collections.synchronizedMap Is Acceptable
If you truly need simple coarse-grained locking, you can wrap a HashMap:
This is valid, but it gives you a single lock around map access. That can be acceptable for low-contention code, but it usually scales worse than ConcurrentHashMap.
Iteration also needs extra care:
Without the surrounding synchronized block, iteration over a synchronized wrapper is still unsafe.
Choose the Fix Based on Ownership
A useful design rule is:
- if a single thread owns the map, keep
HashMap - if many threads share it, prefer
ConcurrentHashMap - if complex multi-step invariants must stay consistent, protect them with a higher-level lock
For example, if two maps must change together, a concurrent map alone is not enough. You probably need a lock around the full operation.
Concurrency bugs are rarely fixed by changing only the collection type. They are fixed by matching the data structure to the access pattern.
Avoid Misleading Workarounds
Developers sometimes try to "fix" the issue by:
- upgrading the JDK without changing the code
- assuming exceptions will reveal every race
- wrapping only one method but not the entire compound operation
None of those solve the underlying correctness problem.
If the map is read-mostly and replaced wholesale, immutable snapshots can be even better than synchronization. But for a general shared mutable map, ConcurrentHashMap is the practical default.
Common Pitfalls
Replacing HashMap with ConcurrentHashMap but keeping non-atomic get then put logic still leaves race conditions in the program.
Using Collections.synchronizedMap and then iterating without synchronizing on the wrapper object is a frequent source of subtle bugs.
Assuming the issue exists only in one JDK version misses the real rule that HashMap has never been a thread-safe mutable map.
Over-synchronizing every operation can fix correctness but create unnecessary contention if a concurrent collection would do the job more cleanly.
Summary
- '
HashMapis not thread-safe, regardless of JDK version.' - For shared mutable maps,
ConcurrentHashMapis usually the correct replacement. - Use atomic methods like
mergeorcomputeIfAbsent, notgetplusput. - '
Collections.synchronizedMapworks, but it is a coarser-grained option and requires synchronized iteration.' - Choose the fix based on the whole concurrency pattern, not just on the collection name.

