Java > Java Collections Framework > List, Set, and Map Interfaces > Concurrent Collections
ConcurrentHashMap Example
This example demonstrates the use of `ConcurrentHashMap` for thread-safe operations on a map. It showcases how multiple threads can concurrently read and write to a map without data corruption or race conditions. The `ConcurrentHashMap` is designed for high concurrency and provides better performance compared to synchronizing access to a regular `HashMap`.
Core Concepts
The `ConcurrentHashMap` is part of the `java.util.concurrent` package and implements the `ConcurrentMap` interface. It achieves thread safety through a combination of techniques including segmentation (dividing the map into multiple segments), lock striping (each segment has its own lock), and copy-on-write strategies for certain operations. This allows multiple threads to access different parts of the map concurrently. Unlike using `Collections.synchronizedMap(new HashMap())`, `ConcurrentHashMap` doesn't lock the entire map for every operation, resulting in improved performance under heavy load.
Code Snippet: ConcurrentHashMap Usage
This code creates a `ConcurrentHashMap` and uses an `ExecutorService` to simulate multiple threads concurrently accessing and modifying the map. The `compute` method is used to atomically update the values associated with keys. The third task demonstrates that reads are non-blocking, allowing the map's size to be accessed concurrently while other threads are writing to it. The `ExecutorService` is shut down gracefully after all tasks are submitted, and `awaitTermination` ensures that all threads complete before the program exits.
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ConcurrentHashMapExample {
public static void main(String[] args) throws InterruptedException {
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newFixedThreadPool(3);
// Task 1: Increment counts for keys A and B
executor.submit(() -> {
for (int i = 0; i < 1000; i++) {
map.compute("A", (key, val) -> (val == null) ? 1 : val + 1);
map.compute("B", (key, val) -> (val == null) ? 1 : val + 1);
}
});
// Task 2: Increment counts for keys C and D
executor.submit(() -> {
for (int i = 0; i < 1000; i++) {
map.compute("C", (key, val) -> (val == null) ? 1 : val + 1);
map.compute("D", (key, val) -> (val == null) ? 1 : val + 1);
}
});
// Task 3: Read and print the size of the map periodically
executor.submit(() -> {
for (int i = 0; i < 5; i++) {
try {
Thread.sleep(200);
System.out.println("Map size: " + map.size());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
System.out.println("Final Map Size: " + map.size());
System.out.println("Map Contents: " + map);
}
}
Explanation of Key Methods
Real-Life Use Case
A common use case for `ConcurrentHashMap` is in caching scenarios where multiple threads need to access and update cached data concurrently. For example, in a web application, you might use a `ConcurrentHashMap` to store session data or frequently accessed database queries. Another scenario is in distributed systems where multiple nodes need to share state information.
Best Practices
Interview Tip
When asked about concurrent collections, be prepared to discuss the differences between `ConcurrentHashMap` and `Collections.synchronizedMap(new HashMap())`. Highlight the performance advantages of `ConcurrentHashMap` due to its fine-grained locking mechanism. Also, mention the atomic operations provided by `ConcurrentHashMap`, such as `compute` and `putIfAbsent`.
When to Use `ConcurrentHashMap`
Use `ConcurrentHashMap` when you need a thread-safe map that provides high concurrency and good performance. It is suitable for scenarios where multiple threads will be accessing and modifying the map concurrently, and where strict consistency is not a requirement.
Memory Footprint
The memory footprint of `ConcurrentHashMap` can be higher than that of a regular `HashMap` due to the additional overhead associated with its internal data structures and locking mechanisms. However, the improved concurrency often outweighs the increased memory usage in many real-world applications.
Alternatives
Pros
Cons
FAQ
-
What is the difference between ConcurrentHashMap and Collections.synchronizedMap?
ConcurrentHashMap provides a higher level of concurrency because it uses fine-grained locking (segment locking) instead of locking the entire map like Collections.synchronizedMap. This allows multiple threads to access different parts of the map concurrently, improving performance under heavy load. -
Does ConcurrentHashMap guarantee strict consistency?
No, ConcurrentHashMap does not guarantee strict consistency. Updates made by one thread may not be immediately visible to other threads. However, it provides a high level of concurrency while maintaining reasonable consistency. -
How do I iterate over a ConcurrentHashMap in a thread-safe manner?
Iterators returned by ConcurrentHashMap are weakly consistent, meaning they reflect the state of the map at some point at or since the creation of the iterator. They are also fail-safe, meaning they don't throw ConcurrentModificationException if the map is modified while iterating.