Java Collections synchronizedMap() Method

The synchronizedMap() method in Java is a utility method provided by the java.util.Collections class. It returns a synchronized (thread-safe) map backed by the specified map. This method is useful when you need to ensure that a map is thread-safe in a concurrent environment.

Table of Contents

  1. Introduction
  2. synchronizedMap() Method Syntax
  3. Examples
    • Basic Usage of synchronizedMap()
    • Using synchronizedMap() with Custom Classes
  4. Real-World Use Case
  5. Conclusion

Introduction

The Collections.synchronizedMap() method provides a way to wrap a given map with synchronized access, ensuring that only one thread can access the map at a time. This is crucial in concurrent applications where multiple threads might try to modify the map simultaneously, leading to race conditions or inconsistent data.

The returned map is a synchronized view of the specified map, meaning that all operations on the map are synchronized using the map’s intrinsic lock. This includes operations such as adding, removing, or iterating over the entries.

synchronizedMap() Method Syntax

The syntax for the synchronizedMap() method is as follows:

public static <K, V> Map<K, V> synchronizedMap(Map<K, V> m)

Parameters:

  • m: The map to be wrapped in a synchronized view.

Returns:

  • A synchronized (thread-safe) map backed by the specified map.

Throws:

  • NullPointerException if the specified map is null.

Examples

Basic Usage of synchronizedMap()

The following example demonstrates how to use the synchronizedMap() method to create a synchronized view of a map.

Example

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class SynchronizedMapExample {
    public static void main(String[] args) {
        // Create a regular HashMap
        Map<String, String> map = new HashMap<>();
        map.put("Apple", "Fruit");
        map.put("Carrot", "Vegetable");
        map.put("Banana", "Fruit");

        // Create a synchronized (thread-safe) map backed by the HashMap
        Map<String, String> synchronizedMap = Collections.synchronizedMap(map);

        // Display the synchronized map
        System.out.println("Synchronized Map: " + synchronizedMap);

        // Use synchronized block for iteration to ensure thread safety
        synchronized (synchronizedMap) {
            Iterator<Map.Entry<String, String>> iterator = synchronizedMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, String> entry = iterator.next();
                System.out.println("Key: " + entry.getKey() + ", Value: " + entry.getValue());
            }
        }

        // Adding and removing entries in a synchronized map
        synchronized (synchronizedMap) {
            synchronizedMap.put("Tomato", "Fruit");
            synchronizedMap.remove("Carrot");
        }

        // Display the modified synchronized map
        System.out.println("Modified Synchronized Map: " + synchronizedMap);
    }
}

Output:

Synchronized Map: {Apple=Fruit, Carrot=Vegetable, Banana=Fruit}
Key: Apple, Value: Fruit
Key: Carrot, Value: Vegetable
Key: Banana, Value: Fruit
Modified Synchronized Map: {Apple=Fruit, Tomato=Fruit, Banana=Fruit}

Using synchronizedMap() with Custom Classes

You can also use the synchronizedMap() method with maps containing instances of custom classes.

Example

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

class Student {
    String name;

    Student(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return name;
    }
}

public class CustomSynchronizedMapExample {
    public static void main(String[] args) {
        // Create a map of students and their scores
        Map<Student, Integer> studentScores = new HashMap<>();
        studentScores.put(new Student("Amit"), 85);
        studentScores.put(new Student("Neha"), 90);
        studentScores.put(new Student("Raj"), 78);

        // Create a synchronized (thread-safe) map backed by the student map
        Map<Student, Integer> synchronizedStudentScores = Collections.synchronizedMap(studentScores);

        // Display the synchronized student map
        System.out.println("Synchronized Student Map: " + synchronizedStudentScores);

        // Use synchronized block for iteration to ensure thread safety
        synchronized (synchronizedStudentScores) {
            Iterator<Map.Entry<Student, Integer>> iterator = synchronizedStudentScores.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<Student, Integer> entry = iterator.next();
                System.out.println("Student: " + entry.getKey() + ", Score: " + entry.getValue());
            }
        }

        // Modifying the synchronized map
        synchronized (synchronizedStudentScores) {
            synchronizedStudentScores.put(new Student("Vikram"), 88);
            synchronizedStudentScores.remove(new Student("Neha"));
        }

        // Display the modified synchronized student map
        System.out.println("Modified Synchronized Student Map: " + synchronizedStudentScores);
    }
}

Output:

Synchronized Student Map: {Neha=90, Amit=85, Raj=78}
Student: Neha, Score: 90
Student: Amit, Score: 85
Student: Raj, Score: 78
Modified Synchronized Student Map: {Neha=90, Amit=85, Raj=78, Vikram=88}

Explanation:

  1. Synchronized View: The synchronizedMap() method returns a synchronized view of the specified map, ensuring thread-safe access.

  2. Synchronized Block: When iterating over the synchronized map, a synchronized block is used to avoid concurrent modification exceptions and ensure thread safety.

  3. Custom Class: The method works with custom class instances, allowing you to create synchronized maps with user-defined objects.

Real-World Use Case

Thread-Safe Access to a Shared Resource

In real-world applications, the synchronizedMap() method can be used to manage thread-safe access to shared resources, such as a map of configuration settings in a multi-threaded environment.

Example

Imagine a scenario where you need to manage a shared map of configuration settings in a concurrent application.

import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

class Configuration {
    String settingName;
    String settingValue;

    Configuration(String settingName, String settingValue) {
        this.settingName = settingName;
        this.settingValue = settingValue;
    }

    @Override
    public String toString() {
        return settingName + "=" + settingValue;
    }
}

public class ConfigurationManager {
    private final Map<String, Configuration> configSettings;

    public ConfigurationManager() {
        // Initialize the configuration map and wrap it in a synchronized view
        this.configSettings = Collections.synchronizedMap(new HashMap<>());
    }

    public void addSetting(String key, Configuration config) {
        synchronized (configSettings) {
            configSettings.put(key, config);
        }
    }

    public void removeSetting(String key) {
        synchronized (configSettings) {
            configSettings.remove(key);
        }
    }

    public void displaySettings() {
        // Use synchronized block for iteration to ensure thread safety
        synchronized (configSettings) {
            Iterator<Map.Entry<String, Configuration>> iterator = configSettings.entrySet().iterator();
            while (iterator.hasNext()) {
                Map.Entry<String, Configuration> entry = iterator.next();
                System.out.println("Key: " + entry.getKey() + ", Configuration: " + entry.getValue());
            }
        }
    }

    public static void main(String[] args) {
        ConfigurationManager configManager = new ConfigurationManager();

        // Add configuration settings to the manager
        configManager.addSetting("timeout", new Configuration("timeout", "30s"));
        configManager.addSetting("maxConnections", new Configuration("maxConnections", "100"));

        // Display all settings
        System.out.println("Configuration Settings:");
        configManager.displaySettings();
    }
}

Output:

Configuration Settings:
Key: timeout, Configuration: timeout=30s
Key: maxConnections, Configuration: maxConnections=100

Explanation:

  1. Configuration Manager: The ConfigurationManager class manages a map of configuration settings, ensuring thread-safe access by wrapping the map in a synchronized view.

  2. Concurrent Environment: The synchronizedMap() method is used to synchronize access to the shared map, preventing race conditions and ensuring data consistency.

Conclusion

The Collections.synchronizedMap() method is a powerful utility for creating synchronized (thread-safe) maps in Java. By providing a simple way to wrap maps with synchronized access, it enhances the flexibility and safety of your code in concurrent environments. This method is particularly valuable in scenarios where you need to manage shared resources or ensure thread-safe access to maps, improving the robustness and maintainability of your Java applications.

Leave a Comment

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

Scroll to Top