ZetCode

Java Collections.synchronizedMap Method

Last modified: April 20, 2025

The Collections.synchronizedMap method creates a thread-safe version of a Map. It wraps the original map and provides synchronized access to all its operations. This is essential for multi-threaded environments.

Synchronized maps ensure that only one thread can access the map at a time. They prevent concurrent modification exceptions and data corruption. However, they may impact performance due to synchronization overhead.

Basic Definitions

A synchronized map is a wrapper around a regular map that adds thread safety. All access to the map is controlled by a single lock. This prevents multiple threads from modifying the map simultaneously.

The synchronization is at the method level. Compound operations still require external synchronization. Iterators must be manually synchronized during use.

Creating a Synchronized Map

This example shows how to create a synchronized map from a HashMap. The Collections.synchronizedMap method takes any Map implementation and returns a thread-safe version.

SynchronizedMapCreation.java
package com.zetcode;

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

public class SynchronizedMapCreation {

    public static void main(String[] args) {
        
        // Create regular HashMap
        Map<String, Integer> unsyncMap = new HashMap<>();
        
        // Create synchronized wrapper
        Map<String, Integer> syncMap = 
            Collections.synchronizedMap(unsyncMap);
        
        // Add elements to synchronized map
        syncMap.put("Apple", 1);
        syncMap.put("Banana", 2);
        syncMap.put("Cherry", 3);
        
        System.out.println("Synchronized map: " + syncMap);
    }
}

This code demonstrates basic synchronized map creation. We first create a regular HashMap, then wrap it using Collections.synchronizedMap. The resulting map can be safely used across multiple threads.

The synchronized map delegates all operations to the original map while adding synchronization. All method calls are atomic and thread-safe.

Multi-threaded Access

This example demonstrates how synchronized maps handle concurrent access. We create multiple threads that modify the map simultaneously. The synchronization prevents data corruption.

SynchronizedMapThreads.java
package com.zetcode;

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

public class SynchronizedMapThreads {

    public static void main(String[] args) throws InterruptedException {
        
        Map<Integer, String> syncMap = 
            Collections.synchronizedMap(new HashMap<>());
        
        // Create and start multiple threads
        Thread[] threads = new Thread[5];
        for (int i = 0; i < threads.length; i++) {
            final int threadId = i;
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 100; j++) {
                    syncMap.put(threadId * 100 + j, 
                        "Thread-" + threadId + "-" + j);
                }
            });
            threads[i].start();
        }
        
        // Wait for all threads to complete
        for (Thread t : threads) {
            t.join();
        }
        
        System.out.println("Map size: " + syncMap.size());
    }
}

This example shows synchronized map usage in a multi-threaded environment. Five threads concurrently add entries to the map. Without synchronization, this would cause data corruption or exceptions.

The synchronized map ensures thread-safe access. Each put operation is atomic. The final size shows all entries were added successfully.

Iterating a Synchronized Map

Iterating over a synchronized map requires manual synchronization. While individual operations are thread-safe, iteration involves multiple calls. This example shows the proper way to iterate.

SynchronizedMapIteration.java
package com.zetcode;

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

public class SynchronizedMapIteration {

    public static void main(String[] args) {
        
        Map<String, Integer> syncMap = 
            Collections.synchronizedMap(new HashMap<>());
        
        syncMap.put("A", 1);
        syncMap.put("B", 2);
        syncMap.put("C", 3);
        
        // Proper synchronized iteration
        synchronized (syncMap) {
            for (Map.Entry<String, Integer> entry : syncMap.entrySet()) {
                System.out.println(entry.getKey() + ": " + entry.getValue());
            }
        }
    }
}

This example demonstrates safe iteration over a synchronized map. We use a synchronized block to ensure the entire iteration is atomic. Without this, concurrent modifications could cause exceptions.

The synchronized block locks the map during iteration. This prevents other threads from modifying the map while we're iterating over it.

Compound Operations

Compound operations on synchronized maps require external synchronization. This example shows how to safely perform check-then-act operations. Individual method calls are atomic, but sequences are not.

SynchronizedMapCompound.java
package com.zetcode;

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

public class SynchronizedMapCompound {

    public static void main(String[] args) {
        
        Map<String, Integer> syncMap = 
            Collections.synchronizedMap(new HashMap<>());
        
        // Unsafe compound operation
        if (!syncMap.containsKey("Apple")) {
            syncMap.put("Apple", 1); // Race condition possible
        }
        
        // Safe compound operation
        synchronized (syncMap) {
            if (!syncMap.containsKey("Banana")) {
                syncMap.put("Banana", 2);
            }
        }
        
        System.out.println("Map: " + syncMap);
    }
}

This example highlights the need for external synchronization with compound operations. The first operation is unsafe as another thread could modify the map between the contains check and the put.

The second operation uses a synchronized block to make the entire sequence atomic. This ensures thread safety for the check-then-act pattern.

Performance Considerations

Synchronized maps have performance overhead due to locking. This example compares synchronized and unsynchronized map performance for read-heavy workloads.

SynchronizedMapPerformance.java
package com.zetcode;

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

public class SynchronizedMapPerformance {

    public static void main(String[] args) {
        
        Map<Integer, String> unsyncMap = new HashMap<>();
        Map<Integer, String> syncMap = 
            Collections.synchronizedMap(new HashMap<>());
        
        // Populate maps
        for (int i = 0; i < 10000; i++) {
            unsyncMap.put(i, "Value" + i);
            syncMap.put(i, "Value" + i);
        }
        
        // Test unsynchronized map
        long start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            unsyncMap.get(i % 10000);
        }
        System.out.println("Unsync time: " + 
            (System.currentTimeMillis() - start) + "ms");
        
        // Test synchronized map
        start = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            syncMap.get(i % 10000);
        }
        System.out.println("Sync time: " + 
            (System.currentTimeMillis() - start) + "ms");
    }
}

This benchmark compares read performance between synchronized and unsynchronized maps. The synchronized map shows higher latency due to lock acquisition.

The difference is more pronounced in highly concurrent scenarios. For write-heavy workloads, consider ConcurrentHashMap for better performance.

Alternative to SynchronizedMap

Java provides ConcurrentHashMap as a modern alternative to synchronized maps. This example compares both approaches for concurrent access.

ConcurrentHashMapExample.java
package com.zetcode;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ConcurrentHashMapExample {

    public static void main(String[] args) throws InterruptedException {
        
        Map<Integer, String> syncMap = 
            Collections.synchronizedMap(new HashMap<>());
        Map<Integer, String> concurrentMap = new ConcurrentHashMap<>();
        
        // Test synchronized map
        long start = System.currentTimeMillis();
        testMap(syncMap);
        System.out.println("SynchronizedMap time: " + 
            (System.currentTimeMillis() - start) + "ms");
        
        // Test ConcurrentHashMap
        start = System.currentTimeMillis();
        testMap(concurrentMap);
        System.out.println("ConcurrentHashMap time: " + 
            (System.currentTimeMillis() - start) + "ms");
    }
    
    private static void testMap(Map<Integer, String> map) 
            throws InterruptedException {
        
        Thread[] threads = new Thread[10];
        for (int i = 0; i < threads.length; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    map.put(j, "Value");
                    map.get(j);
                }
            });
            threads[i].start();
        }
        
        for (Thread t : threads) {
            t.join();
        }
    }
}

This example demonstrates the performance difference between synchronized maps and ConcurrentHashMap. ConcurrentHashMap uses finer-grained locking for better throughput in concurrent scenarios.

For high-concurrency applications, ConcurrentHashMap is generally preferred. It provides better scalability while maintaining thread safety.

Source

Java Collections.synchronizedMap Documentation

In this article, we've explored Java's Collections.synchronizedMap method in depth. We covered creation, multi-threaded access, iteration, compound operations, and performance considerations. Understanding these concepts is crucial for building thread-safe applications.

Author

My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.

List all Java tutorials.