Open In App

Create Thread-safe HashMap Without Using Collections.synchronizedMap in Java

Last Updated : 31 Jan, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

The HashMap class in Java is a commonly used data structure for storing key-value pairs. While HashMap provides fast and efficient operations, it is not inherently thread-safe. When working in a multi-threaded environment, it’s crucial to ensure that data structures are accessed and modified in a way that prevents race conditions and maintains consistency.

In this article, we will learn How to create a thread-safe HashMap without using Collections.synchronizedMap in Java.

Thread-Safe HashMap Without Using Collections.synchronizedMap in Java

One commonly used approach to achieving thread safety with a HashMap is to use the Collections.synchronizedMap method. However, there’s an alternative method to create a thread-safe HashMap without relying on this utility.

The Challenge of Thread Safety: In a multi-threaded environment, simultaneous access to a non-thread-safe HashMap can lead to data corruption and unpredictable behavior. To address this, developers often synchronize access to the HashMap using techniques such as Collections.synchronizedMap or manual synchronization with synchronized blocks.

1. Using ConcurrentHashMap – An Alternative

An alternative to explicit synchronization is to use the ConcurrentHashMap class, introduced in Java 5. ConcurrentHashMap is specifically designed for concurrent access and provides better performance in comparison to synchronized maps.

Here’s an example of how you can create and use a thread-safe HashMap using ConcurrentHashMap:

Java




// Java program demonstrating the use of
// ConcurrentHashMap for thread-safe operations
import java.util.concurrent.ConcurrentHashMap;
  
// Class definition
public class ThreadSafeHashMapExample {
  
    // Main method
    public static void main(String[] args)
    {
        // Creating a thread-safe HashMap
        // without using Collections.synchronizedMap
        ConcurrentHashMap<String, Integer> threadSafeMap = new ConcurrentHashMap<>();
  
        // Adding key-value pairs to the ConcurrentHashMap
        threadSafeMap.put("One", 1);
        threadSafeMap.put("Two", 2);
        threadSafeMap.put("Three", 3);
  
        // Accessing and printing values using forEach and a
        // lambda expression
        threadSafeMap.forEach((key, value) -> System.out.println(key + ": " + value));
    }
}


Output:

One: 1
Two: 2
Three: 3

In this example, the ConcurrentHashMap is used instead of a regular HashMap. The operations on this map are inherently thread-safe, eliminating the need for external synchronization.

2. Implementing Custom Thread-Safe HashMap

If you want to create a thread-safe HashMap without using ConcurrentHashMap, you can implement your own custom solution. One approach is to use locks to synchronize critical sections of code:

Java




// Java program demonstrating a custom
// thread-safe HashMap using explicit locks
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
  
// CustomThreadSafeHashMap with generic
// key (K) and value (V) types
public class CustomThreadSafeHashMap<K, V> {
  
    // Private instance variables
    private final Map<K, V> map = new HashMap<>();
    private final Lock lock = new ReentrantLock();
  
    // Method to add a key-value pair
      // to the map in a thread-safe manner
    public void put(K key, V value) {
      // Acquiring the lock
        lock.lock(); 
        try {
            // Performing the put operation on the map
            map.put(key, value);
        } finally {
            // Releasing the lock in a finally block
              // to ensure it happens even if an exception occurs
            lock.unlock(); 
        }
    }
  
    // Method to retrieve a value associated
      // with a key in a thread-safe manner
    public V get(K key) {
          // Acquiring the lock
        lock.lock(); 
        try {
              // Performing the get operation on the map
            return map.get(key); 
        } finally {
              // Releasing the lock in a finally block
              // to ensure it happens even if an exception occurs
            lock.unlock(); 
        }
    }
  
    // Main method for testing the CustomThreadSafeHashMap
    public static void main(String[] args) {
        // Creating an instance of CustomThreadSafeHashMap
        // with String keys and Integer values
        CustomThreadSafeHashMap<String, Integer> customThreadSafeMap = new CustomThreadSafeHashMap<>();
  
        // Adding key-value pairs to the custom thread-safe map
        customThreadSafeMap.put("One", 1);
        customThreadSafeMap.put("Two", 2);
        customThreadSafeMap.put("Three", 3);
  
        // Accessing and printing values associated with specific keys
        System.out.println("Value for key 'One': " + customThreadSafeMap.get("One"));
        System.out.println("Value for key 'Two': " + customThreadSafeMap.get("Two"));
        System.out.println("Value for key 'Three': " + customThreadSafeMap.get("Three"));
    }
}


Output:

Value for key 'One': 1
Value for key 'Two': 2
Value for key 'Three': 3

In this example, a ReentrantLock is used to synchronize access to the HashMap. Each critical section of code is enclosed within lock and unlock calls, ensuring that only one thread can access the map at a time.



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads