ConcurrentHashMap in Java

In Java, ConcurrentHashMap is a class that implements the ConcurrentMap interface and provides a concurrent, thread-safe implementation of a hash table. Multiple threads can access and modify the map simultaneously without explicit synchronization when used in concurrent environments.

ConcurrentHashMap example

Here’s a detailed example of how to use ConcurrentHashMap:

import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {
    public static void main(String[] args) {
        // Create a new ConcurrentHashMap
        ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

        // Add key-value pairs to the map
        map.put("One", 1);
        map.put("Two", 2);
        map.put("Three", 3);

        // Retrieve values from the map
        int value1 = map.get("One");
        System.out.println("Value for key 'One': " + value1);

        // Check if a key exists in the map
        boolean containsKey = map.containsKey("Two");
        System.out.println("Contains key 'Two': " + containsKey);

        // Update the value for a key
        map.put("Three", 4);
        int updatedValue = map.get("Three");
        System.out.println("Updated value for key 'Three': " + updatedValue);

        // Remove a key-value pair from the map
        int removedValue = map.remove("One");
        System.out.println("Removed value for key 'One': " + removedValue);

        // Iterate over the map
        for (String key : map.keySet()) {
            int value = map.get(key);
            System.out.println("Key: " + key + ", Value: " + value);
        }
    }
}

In this example, we create a ConcurrentHashMap named map. We then add key-value pairs to the map using the put method. The keys are strings, and the values are integers.

We retrieve values from the map using the get method, passing the key as an argument. We can check if a key exists in the map using the containsKey method.

We can update the value associated with a key by calling the put method again with the same key but a different value. We can remove a key-value pair from the map using the remove method.

To iterate over the map, we use the keySet method to obtain a set of all keys in the map. We then iterate over this set, retrieve the value for each key, and print the key-value pairs.

The ConcurrentHashMap looks like normal HashMap but it provides thread-safe operations, allowing multiple threads to access and modify the map concurrently without explicit synchronization. This makes it suitable for scenarios where multiple threads need to work with a shared map simultaneously.

Feature of ConcurrentHashmap

  • Thread-Safety: ConcurrentHashMap allows multiple threads to access and modify the map concurrently without explicit synchronization. It ensures thread-safety by using internal locking mechanisms that partition the map into segments, allowing multiple threads to operate on different segments simultaneously.
  • High Concurrency: It is designed for high-concurrency scenarios, where multiple threads can perform concurrent read and write operations on the map without causing data inconsistencies or conflicts. This makes it suitable for multi-threaded environments.
  • Performance: ConcurrentHashMap is optimized for performance and scalability. It provides efficient concurrent operations with minimal contention, allowing for improved throughput in highly concurrent scenarios. It achieves this by dividing the map into segments, reducing the scope of locking and contention.
  • Key-Value Pairs: It stores data as key-value pairs, where each key must be unique. Keys and values can be of any non-null reference type.
  • Basic Operations: ConcurrentHashMap supports common map operations such as put, get, remove, containsKey, and size. It provides atomic operations for individual operations and ensures consistency in the presence of concurrent modifications.
  • Iteration: It allows iteration over the map using the keySet, values, or entrySet methods. Iteration is weakly consistent, meaning it reflects the state of the map at some point during the iteration but does not guarantee real-time visibility of concurrent updates.
  • Expansion and Resizing: ConcurrentHashMap automatically expands and resizes itself when the number of elements exceeds a certain threshold, maintaining a load factor of approximately 0.75. This ensures efficient space utilization and performance.
  • Not Ordered: The iteration order of ConcurrentHashMap is not guaranteed to be in any particular order. If you need a sorted map, you should consider using ConcurrentSkipListMap instead.

Usage of ConcurrentHashmap

Concurrent Access

ConcurrentHashMap allows concurrent access to the map by multiple threads without explicit synchronization. This means that multiple threads can read, write, and update the map simultaneously.

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

// Multiple threads accessing the map concurrently
ExecutorService executor = Executors.newFixedThreadPool(5);

executor.submit(() -> {
    map.put("One", 1);
});

executor.submit(() -> {
    map.put("Two", 2);
});

executor.submit(() -> {
    int value = map.get("One");
    System.out.println("Value for key 'One': " + value);
});

executor.shutdown();

In this example, we create a ConcurrentHashMap and submit multiple tasks to an ExecutorService. Each task performs operations on the map concurrently. One task adds a key-value pair, another task adds a different key-value pair, and a third task retrieves the value associated with a key. Since ConcurrentHashMap provides thread-safe operations, these tasks can run concurrently without causing data inconsistencies or exceptions.

Atomic Operations

ConcurrentHashMap guarantees atomic operations without interfering with other threads.

ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();

map.put("Counter", 0);

// Multiple threads incrementing a counter atomically
ExecutorService executor = Executors.newFixedThreadPool(5);

for (int i = 0; i < 10; i++) {
    executor.submit(() -> {
        map.compute("Counter", (key, value) -> value + 1);
    });
}

executor.shutdown();
executor.awaitTermination(1, TimeUnit.SECONDS);

int counterValue = map.get("Counter");
System.out.println("Counter value: " + counterValue);

In this example, we have a counter stored in the ConcurrentHashMap. Multiple threads are concurrently incrementing the counter using the compute method with a lambda function. The lambda function takes the current value associated with the key “Counter” and increments it by 1. Since ConcurrentHashMap performs the computation atomically, we can be sure that the counter is incremented correctly, even in a concurrent environment.

Bulk Operations

ConcurrentHashMap supports bulk operations such as putAll, replaceAll, and forEach. Multiple threads can do these operations at the same time without having to work together.

ConcurrentHashMap<String, Integer> sourceMap = new ConcurrentHashMap<>();
sourceMap.put("One", 1);
sourceMap.put("Two", 2);

ConcurrentHashMap<String, Integer> targetMap = new ConcurrentHashMap<>();
targetMap.put("Three", 3);

// Multiple threads copying key-value pairs from sourceMap to targetMap
ExecutorService executor = Executors.newFixedThreadPool(5);

executor.submit(() -> {
    targetMap.putAll(sourceMap);
});

executor.submit(() -> {
    targetMap.replaceAll((key, value) -> value * 10);
});

executor.submit(() -> {
    targetMap.forEach((key, value) -> System.out.println(key + ": " + value));
});

executor.shutdown();
executor.awaitTermination(1, TimeUnit.SECONDS);

In this example, we have a source ConcurrentHashMap with key-value pairs, and a target ConcurrentHashMap initially containing a different key-value pair. Multiple threads concurrently perform operations on the target map. One thread uses putAll to copy all key-value pairs from

Good choice and Bad choice for using ConcurrentHashMap

In scenarios where multiple threads need to access and modify a shared map simultaneously, ConcurrentHashMap is a good option.

Where to use

Here are some practical scenarios where you should consider using ConcurrentHashMap:

  • Multi-threaded Environments: When you have a multi-threaded application where multiple threads read from and write to a shared map, ConcurrentHashMap ensures thread-safety without the need for explicit synchronization. It allows concurrent access and modifications, reducing contention and improving performance.
  • Caching: ConcurrentHashMap can be used as a thread-safe cache implementation. Multiple threads can concurrently access the cache to store and retrieve values without causing data inconsistencies or concurrency issues. It provides atomic operations that are useful for cache-related tasks.
  • Parallel Processing: If you have a parallel processing scenario where you partition the work across multiple threads, ConcurrentHashMap can be used to collect the results. Each thread can update the map with its partial results without worrying about synchronization issues.
  • High-Concurrency Scenarios: In scenarios with high levels of concurrency, where the number of threads accessing the map is significant, ConcurrentHashMap offers better scalability and performance compared to using traditional synchronized maps. It optimizes the concurrency control to minimize contention and allows multiple threads to operate concurrently.

Where NOT to use

However, there are some scenarios where ConcurrentHashMap might not be the best choice:

  • Single-Threaded Environments: If your application runs in a single-threaded environment where only one thread accesses the map, there is no need for the additional overhead and complexity introduced by ConcurrentHashMap. In such cases, a regular HashMap or Hashtable would be more appropriate.
  • Immutable Data: If your map does not require modifications after initialization and contains immutable data, a regular HashMap or an immutable map implementation, such as Collections.unmodifiableMap, would be sufficient. ConcurrentHashMap provides additional overhead and complexity for concurrent modifications that are unnecessary in this case.
  • Performance-Critical Operations: For certain performance-critical operations where strict synchronization is not required, using ConcurrentHashMap might introduce unnecessary overhead due to its thread-safe nature. In such cases, a non-thread-safe data structure or a custom synchronization mechanism may be more suitable.
  • Strong Ordering Requirements: ConcurrentHashMap does not provide strong ordering guarantees for its iterators and views. If you require strict ordering of entries, you might need to consider alternative data structures, such as TreeMap, that provide the desired ordering semantics.

It’s important to evaluate your specific use case and consider the concurrency requirements, performance characteristics, and ordering needs before deciding to use ConcurrentHashMap or opting for other data structures.

Alternative of ConcurrentHashMap

An alternative to ConcurrentHashMap in Java is the ConcurrentSkipListMap class. ConcurrentSkipListMap provides a concurrent and sorted map implementation, similar to ConcurrentHashMap, but with the added feature of maintaining elements in sorted order based on their keys.

import java.util.concurrent.ConcurrentSkipListMap;

public class ConcurrentSkipListMapExample {
    public static void main(String[] args) {
        // Create a new ConcurrentSkipListMap
        ConcurrentSkipListMap<Integer, String> map = new ConcurrentSkipListMap<>();

        // Add key-value pairs to the map
        map.put(3, "Three");
        map.put(1, "One");
        map.put(2, "Two");

        // Retrieve values from the map
        String value1 = map.get(1);
        System.out.println("Value for key 1: " + value1);

        // Iterate over the map
        for (Integer key : map.keySet()) {
            String value = map.get(key);
            System.out.println("Key: " + key + ", Value: " + value);
        }
    }
}

In this example, we create a ConcurrentSkipListMap named map. We add key-value pairs to the map, where the keys are integers and the values are strings. Unlike ConcurrentHashMap, the elements in ConcurrentSkipListMap are automatically ordered based on their keys.

We can retrieve values from the map using the get method, passing the key as an argument. We can also iterate over the map using the keySet method to obtain a set of keys and retrieve the corresponding values.

ConcurrentSkipListMap is particularly useful when you require both concurrent access and sorted order of elements based on their keys. It provides similar thread-safe operations as ConcurrentHashMap, such as put, get, and remove, while maintaining the sorted order of the elements.

It’s important to note that ConcurrentSkipListMap might have slightly lower performance compared to ConcurrentHashMap for non-sorted operations due to the additional overhead of maintaining the sorted order. Therefore, you should choose ConcurrentSkipListMap when sorted order is a priority and consider ConcurrentHashMap for scenarios where ordering is not essential.

Summing Up

  • ConcurrentHashMap allows multiple threads to access it and modify it simultaneously without explicit synchronization in concurrent environments.
  • ConcurrentHashMap provides a powerful and efficient solution for concurrent access and modifications to a hash table,
  • It provides efficient concurrent operations with minimal contention.
  • Although its features and benefits, there are certain scenarios where ConcurrentHashMap is not best fit.
  • You can find more detail about ConcurrentHashMap from the link.

 

Recommended For You

About the Author: Nilang

Nilang Patel is a technology evangelist who loves to spread knowledge and helping people in all possible ways. He is an author of two technical books - Java 9 Dependency and Spring 5.0 Projects.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.