Java

ConcurrentHashMap Internals

Master Java's most important concurrent collection. Understand how ConcurrentHashMap evolved from segment-based locking to CAS operations, why it dramatically outperforms synchronized maps, and how to use it correctly for thread-safe operations.

πŸ“‹ At a Glance

AspectDetails
TopicConcurrentHashMap, concurrent operations, thread safety
ComplexityAdvanced
PrerequisitesPart 12 (HashMap Internals), basic concurrency knowledge
Time to Master4-5 hours
Interview FrequencyVery High (thread safety, CAS operations, compound actions)

🎯 What You'll Learn

After completing this article, you will be able to:

  1. Understand ConcurrentHashMap's lock-free read operations
  2. Master the evolution from Java 7 segments to Java 8+ CAS
  3. Use atomic compound operations (compute, merge) correctly
  4. Avoid common concurrency pitfalls with ConcurrentHashMap
  5. Choose between ConcurrentHashMap and synchronized alternatives

Interactive Visualizer

Watch how ConcurrentHashMap handles concurrent access with segment-level locking - multiple threads can write to different segments simultaneously:

Loading visualizer...

Production Story: The HashMap Race Condition Disaster

The Incident

Our session management service was corrupting user data in production. Users were being logged into wrong accounts, seeing other users' data, and experiencing random authentication failures.

JAVA(16 lines)
Code
Loading syntax highlighter...

The Race Condition

TEXT(15 lines)
Code
Loading syntax highlighter...

The ConcurrentHashMap Fix

JAVA(21 lines)
Code
Loading syntax highlighter...

Performance Comparison

JAVA(11 lines)
Code
Loading syntax highlighter...

Why ConcurrentHashMap is Faster

  1. Lock-free reads: get() uses volatile reads, no locking
  2. Per-bin locking: Only lock the specific bucket, not entire map
  3. CAS operations: Atomic updates without locks when possible
  4. Optimistic concurrency: Assume success, handle conflicts

Mental Model: The Restaurant Kitchen

TEXT(28 lines)
Code
Loading syntax highlighter...

Deep Dive: Evolution of ConcurrentHashMap

Java 7: Segment-Based Locking

JAVA(14 lines)
Code
Loading syntax highlighter...

Java 8+: CAS + Per-Bin Locking

JAVA(11 lines)
Code
Loading syntax highlighter...

Java 8+ Key Improvements

TEXT(19 lines)
Code
Loading syntax highlighter...

Deep Dive: ConcurrentHashMap Operations

Basic Thread-Safe Operations

JAVA(18 lines)
Code
Loading syntax highlighter...

Atomic Compound Operations (Java 8+)

JAVA(19 lines)
Code
Loading syntax highlighter...

Bulk Operations (Java 8+)

JAVA(24 lines)
Code
Loading syntax highlighter...

Deep Dive: Internal Operations

CAS (Compare-And-Swap)

JAVA(16 lines)
Code
Loading syntax highlighter...

Lock-Free Read

JAVA(24 lines)
Code
Loading syntax highlighter...

Concurrent Resize

JAVA(13 lines)
Code
Loading syntax highlighter...

Deep Dive: Common Patterns

Pattern 1: Concurrent Counter

JAVA(15 lines)
Code
Loading syntax highlighter...

Pattern 2: Cache with Expiration

JAVA(41 lines)
Code
Loading syntax highlighter...

Pattern 3: Concurrent Set

JAVA(9 lines)
Code
Loading syntax highlighter...

Pattern 4: Memoization

JAVA(18 lines)
Code
Loading syntax highlighter...

Deep Dive: Weakly Consistent Iterators

Iterator Behavior

JAVA(17 lines)
Code
Loading syntax highlighter...

Implications

JAVA(12 lines)
Code
Loading syntax highlighter...

⚠️ Common Mistakes

Mistake 1: Non-Atomic Check-Then-Act

JAVA(14 lines)
Code
Loading syntax highlighter...

Mistake 2: Modifying Map in computeIfAbsent

JAVA(11 lines)
Code
Loading syntax highlighter...

Mistake 3: Assuming size() is Accurate

JAVA(13 lines)
Code
Loading syntax highlighter...

Mistake 4: Using null Keys or Values

JAVA(10 lines)
Code
Loading syntax highlighter...

Mistake 5: Replacing with Collections.synchronizedMap

JAVA(12 lines)
Code
Loading syntax highlighter...

πŸ› Debug This

Challenge 1: The Lost Updates

JAVA(15 lines)
Code
Loading syntax highlighter...
βœ… Answer:
Output will be less than 1000 (unpredictable, maybe 100-500).

The get-then-put is not atomic. Multiple threads read the same value, all increment to the same result, losing updates.

Fix:
JAVA(2 lines)
Code
Loading syntax highlighter...

Challenge 2: The Deadlock

JAVA(5 lines)
Code
Loading syntax highlighter...
βœ… Answer:
This may cause deadlock or throw IllegalStateException depending on Java version!

In Java 8, nested computeIfAbsent on the same map can deadlock if both keys hash to the same bin (which gets locked).

In Java 9+, ConcurrentHashMap detects recursive compute and throws IllegalStateException.

Fix: Never modify the map inside compute functions.

Challenge 3: The Inconsistent View

JAVA(10 lines)
Code
Loading syntax highlighter...
βœ… Answer:
Output is either 3 or 6 (unpredictable).
  • If iterator doesn't see "C": sum = 1 + 2 = 3
  • If iterator sees "C": sum = 1 + 2 + 3 = 6

The iterator is weakly consistent - it may or may not see concurrent modifications.


πŸ’» Exercises

Exercise 1: Thread-Safe Counter Map

Implement a thread-safe word counter:

JAVA(5 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(18 lines)
Code
Loading syntax highlighter...

Exercise 2: Concurrent Cache with Loading

Implement a cache that loads values on cache miss:

JAVA(6 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(24 lines)
Code
Loading syntax highlighter...

Exercise 3: Rate Limiter

Implement a thread-safe rate limiter:

JAVA(4 lines)
Code
Loading syntax highlighter...
βœ… Solution:
JAVA(40 lines)
Code
Loading syntax highlighter...

🎀 Senior-Level Interview Questions

Question 1: HashMap vs ConcurrentHashMap Thread Safety

Q: Why is HashMap not thread-safe, and what specific problems occur?
A:

HashMap issues under concurrency:

  1. Lost updates: Two threads read same value, both write β†’ one lost
  2. Infinite loop: Resize during concurrent insert β†’ circular linked list
  3. Corrupted data: Partial writes visible to other threads
  4. Null returns: Entry in wrong bucket after concurrent resize
JAVA(5 lines)
Code
Loading syntax highlighter...

Question 2: CAS vs Locking

Q: When does ConcurrentHashMap use CAS vs synchronized?
A:
CAS (lock-free):
  • Adding first node to empty bin
  • Updating single values atomically
  • Size counting (using LongAdder internally)
synchronized (locking):
  • Adding to non-empty bin (collision handling)
  • Tree operations (when bin is TreeBin)
  • Resize operations
JAVA(8 lines)
Code
Loading syntax highlighter...

Question 3: Weakly Consistent Semantics

Q: What does "weakly consistent" mean for ConcurrentHashMap iterators?
A:

Guarantees:

  • No ConcurrentModificationException
  • Elements returned at most once
  • Elements present for entire iteration will be seen

No guarantees:

  • Seeing modifications during iteration
  • Consistent snapshot of all elements
  • Specific ordering
JAVA(6 lines)
Code
Loading syntax highlighter...

Question 4: computeIfAbsent vs putIfAbsent

Q: What's the difference and when to use each?
A:
JAVA(17 lines)
Code
Loading syntax highlighter...

Question 5: Concurrent Resize

Q: How does ConcurrentHashMap handle resizing without blocking all operations?
A:
  1. ForwardingNode marker: Indicates "look in new table"
  2. Work stealing: Multiple threads help transfer bins
  3. Gradual transfer: Bins transferred incrementally
  4. Read during resize: Reads check both tables
JAVA(5 lines)
Code
Loading syntax highlighter...

Question 6: size() vs mappingCount()

Q: Why does ConcurrentHashMap have both size() and mappingCount()?
A:
JAVA(12 lines)
Code
Loading syntax highlighter...

πŸ“ Summary & Key Takeaways

Thread Safety Evolution

  • Java 7: Segment-based locking (16 segments default)
  • Java 8+: Per-bin locking + CAS operations
  • Lock-free reads, fine-grained write locking

Key Operations

  • get(), containsKey(): Lock-free (volatile reads)
  • put(), remove(): CAS for empty bins, synchronized for collisions
  • computeIfAbsent(), merge(): Atomic compound operations

Common Patterns

  • Counter: merge() or LongAdder for high contention
  • Cache: computeIfAbsent() for lazy loading
  • Set: ConcurrentHashMap.newKeySet()

Pitfalls to Avoid

  • Don't use check-then-act (not atomic)
  • Don't modify map inside compute functions
  • Don't rely on size() for logic
  • Don't use null keys or values

🏁 Conclusion

ConcurrentHashMap is the backbone of concurrent Java applications. Its evolution from segment-based locking to CAS operations demonstrates sophisticated engineering for scalable concurrency. Understanding its internals helps you use it correctly and avoid subtle bugs.

Key takeaways:

  1. Lock-free reads make ConcurrentHashMap extremely fast for read-heavy workloads
  2. Atomic operations (compute, merge) prevent race conditions
  3. Never use check-then-act patterns - use atomic operations instead
  4. Iterators are weakly consistent - may or may not see concurrent modifications
  5. Don't modify map inside compute - can cause deadlock

In the next article, we'll explore ConcurrentHashMap's advanced features including bulk operations, custom reductions, and integration with parallel streams.


πŸ“… Review Schedule

To solidify your understanding, review this material:

  • Tomorrow: Practice atomic operations (compute, merge)
  • In 3 days: Implement concurrent counter pattern
  • In 1 week: Explain CAS vs locking to a colleague
  • In 2 weeks: Review all pitfalls and common mistakes