Java ConcurrentHashMap
In concurrent programming, ensuring thread safety is crucial when multiple threads interact with shared data structures. The ConcurrentHashMap
class in Java, part of the java.util.concurrent
package, provides a high-performance, thread-safe implementation of the ConcurrentMap
interface. It allows multiple threads to read and write to the map concurrently without the risk of data corruption, all while maintaining efficient performance.
This article explores the ConcurrentHashMap
class, its key features, and how to use it effectively for building multi-threaded applications in Java.
ConcurrentHashMap
is a thread-safe map implementation that allows concurrent access to different segments of the map without the need for global synchronization. It is part of the java.util.concurrent
package and is designed to handle large-scale concurrent access, making it ideal for applications with multiple threads accessing and modifying a shared map.
Unlike traditional synchronized maps (like HashMap
wrapped with Collections.synchronizedMap()
), ConcurrentHashMap
provides higher concurrency and better performance because it allows multiple threads to access different segments of the map simultaneously.
putIfAbsent()
, remove()
, replace()
, and computeIfAbsent()
, preventing race conditions.Map
implementations, ConcurrentHashMap
does not allow null
keys or values.The ConcurrentHashMap
class extends AbstractMap
and implements ConcurrentMap
. Some of its important methods include:
This method adds a key-value pair to the map only if the specified key is not already present.
V putIfAbsent(K key, V value);
Removes the entry for a specific key only if it is currently mapped to the specified value.
boolean remove(Object key, Object value);
Replaces the value associated with a key only if the current value is equal to the expected oldValue
.
boolean replace(K key, V oldValue, V newValue);
Replaces the value associated with the specified key, if the key is already present.
V replace(K key, V value);
This method computes the value for the specified key using the provided function only if the key is not already present in the map.
V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction);
This method computes the value for the specified key using the provided function only if the key is already present in the map.
V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
This method computes a new value for the specified key using the provided function.
V compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
This method iterates over all the key-value pairs in the map and performs the given action on each entry.
void forEach(BiConsumer<? super K, ? super V> action);
Here’s an example demonstrating how to use ConcurrentHashMap
in a multi-threaded Java application:
import java.util.concurrent.*;
public class ConcurrentHashMapExample {
public static void main(String[] args) {
// Create a ConcurrentHashMap
ConcurrentMap<String, Integer> map = new ConcurrentHashMap<>();
// Add entries using putIfAbsent (atomic operation)
map.putIfAbsent("Apple", 3);
map.putIfAbsent("Banana", 2);
map.putIfAbsent("Mango", 1);
// Print the map
System.out.println("Map after adding entries: " + map);
// Replace an entry if present
map.replace("Apple", 5);
System.out.println("Map after replacing Apple: " + map);
// Compute a value if the key is absent
map.computeIfAbsent("Orange", key -> 4);
System.out.println("Map after computing value for Orange: " + map);
// Compute a value if the key is present
map.computeIfPresent("Banana", (key, value) -> value * 2);
System.out.println("Map after computing value for Banana: " + map);
// Remove an entry if it matches the expected value
map.remove("Mango", 1);
System.out.println("Map after removing Mango: " + map);
// Iterate over all entries using forEach
map.forEach((key, value) -> System.out.println(key + ": " + value));
}
}
Output:
Map after adding entries: {Apple=3, Banana=2, Mango=1}
Map after replacing Apple: {Apple=5, Banana=2, Mango=1}
Map after computing value for Orange: {Apple=5, Banana=2, Mango=1, Orange=4}
Map after computing value for Banana: {Apple=5, Banana=4, Mango=1, Orange=4}
Map after removing Mango: {Apple=5, Banana=4, Orange=4}
Apple: 5
Banana: 4
Orange: 4
putIfAbsent()
adds an entry only if the key does not already exist.replace()
updates the value associated with a key if it is present.computeIfAbsent()
computes a value for a key if the key is not already in the map.computeIfPresent()
computes a value for a key if it already exists.remove()
removes an entry only if the current value matches the specified value.forEach()
iterates over all entries in a thread-safe manner.ConcurrentHashMap
achieves thread safety using a technique called bucket-based concurrency. The map is divided into several segments, and each segment can be locked independently, allowing for high concurrency and performance. Specifically:
This approach helps to minimize the contention between threads, making ConcurrentHashMap
highly efficient in a multi-threaded environment.
Use ConcurrentHashMap
in Java when:
putIfAbsent()
, replace()
, and compute()
are needed to avoid race conditions.Here’s a comparison of ConcurrentHashMap
and HashMap
:
Feature | ConcurrentHashMap |
HashMap |
---|---|---|
Thread Safety | Thread-safe for concurrent operations. | Not thread-safe; needs synchronization. |
Null Keys/Values | Does not allow null keys or values. |
Allows null keys and values. |
Concurrency | Allows concurrent reads and writes with multiple threads. | Not designed for multi-threaded use. |
Performance | Higher performance in multi-threaded environments. | Suitable for single-threaded environments. |