Java Collections.synchronizedMap 方法
上次修改时间:2025 年 4 月 20 日
Collections.synchronizedMap
方法创建 Map 的线程安全版本。它封装了原始的 Map 并提供对其所有操作的同步访问。这对于多线程环境至关重要。
同步 Map 确保一次只有一个线程可以访问 Map。它们防止并发修改异常和数据损坏。但是,由于同步开销,它们可能会影响性能。
基本定义
同步 Map 是对普通 Map 的一个包装,增加了线程安全性。对 Map 的所有访问都由单个锁控制。这可以防止多个线程同时修改 Map。
同步发生在方法级别。复合操作仍然需要外部同步。迭代器在使用期间必须手动同步。
创建同步 Map
此示例展示了如何从 HashMap 创建一个同步 Map。Collections.synchronizedMap
方法接受任何 Map 实现,并返回一个线程安全版本。
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); } }
此代码演示了基本的同步 Map 创建。我们首先创建一个普通的 HashMap,然后使用 Collections.synchronizedMap
进行包装。生成的 Map 可以在多个线程之间安全使用。
同步 Map 将所有操作委托给原始 Map,同时添加同步。所有方法调用都是原子的和线程安全的。
多线程访问
此示例演示了同步 Map 如何处理并发访问。我们创建多个线程,这些线程同时修改 Map。同步可以防止数据损坏。
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()); } }
此示例展示了同步 Map 在多线程环境中的使用。五个线程并发地向 Map 中添加条目。如果没有同步,这将导致数据损坏或异常。
同步 Map 确保了线程安全的访问。每个 put 操作都是原子的。最终大小显示所有条目都已成功添加。
迭代同步 Map
迭代同步 Map 需要手动同步。虽然单个操作是线程安全的,但迭代涉及多个调用。此示例展示了正确的迭代方式。
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()); } } } }
此示例演示了对同步 Map 的安全迭代。我们使用同步块来确保整个迭代是原子的。如果没有这样做,并发修改可能会导致异常。
同步块在迭代期间锁定 Map。这可以防止其他线程在我们在迭代 Map 时修改它。
复合操作
对同步 Map 的复合操作需要外部同步。此示例展示了如何安全地执行检查后操作。单个方法调用是原子的,但序列不是。
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); } }
此示例强调了复合操作需要外部同步。第一个操作是不安全的,因为另一个线程可能在 contains 检查和 put 之间修改 Map。
第二个操作使用同步块使整个序列成为原子操作。这确保了检查后操作模式的线程安全性。
性能注意事项
同步 Map 由于锁定而具有性能开销。此示例比较了同步和不同步 Map 在读密集型工作负载中的性能。
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"); } }
此基准测试比较了同步和不同步 Map 之间的读取性能。同步 Map 显示出更高的延迟,这是由于获取锁造成的。
在高度并发的场景中,差异更加明显。对于写密集型工作负载,请考虑使用 ConcurrentHashMap 以获得更好的性能。
同步 Map 的替代方案
Java 提供了 ConcurrentHashMap 作为同步 Map 的现代替代方案。此示例比较了这两种方法在并发访问中的使用。
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(); } } }
此示例演示了同步 Map 和 ConcurrentHashMap 之间的性能差异。ConcurrentHashMap 使用更细粒度的锁定,以在并发场景中获得更好的吞吐量。
对于高并发应用程序,通常更倾向于使用 ConcurrentHashMap。它在保持线程安全的同时提供了更好的可伸缩性。
来源
Java Collections.synchronizedMap 文档
在本文中,我们深入探讨了 Java 的 Collections.synchronizedMap
方法。我们介绍了创建、多线程访问、迭代、复合操作和性能注意事项。理解这些概念对于构建线程安全的应用程序至关重要。
作者
列出所有Java教程。