java中collections.synchronizedmap()提供方法级同步,但存在迭代需手动同步、复合操作非原子、高并发性能差等问题,推荐优先使用concurrenthashmap。

Java中直接使用HashMap等非线程安全集合在多线程环境下会导致数据不一致甚至抛出ConcurrentModificationException。虽然Collections.synchronizedMap()能快速获得线程安全的包装版本,但它并非“开箱即用”的万能方案——其同步粒度、迭代风险和性能瓶颈常被低估。
同步包装的本质:方法级synchronized锁
Collections.synchronizedMap()返回的是一个内部装饰器(如SynchronizedMap),它对原始Map所有公开方法(get、put、remove等)加了synchronized(this)。这意味着:
- 每次调用都是独占锁,同一时间只允许一个线程执行任意一个操作
- 锁对象是包装器实例本身,不是底层map,也不支持自定义锁粒度
- 多个独立操作(如先
containsKey()再get())之间不构成原子性,需手动同步
迭代时必须显式同步,否则仍可能出错
这是最易踩的坑:entrySet()、keySet()、values()返回的集合视图本身不是线程安全的。即使map被同步包装,遍历时若其他线程修改map,仍会触发ConcurrentModificationException。
正确做法是手动同步整个迭代过程:
Map<string integer> syncMap = Collections.synchronizedMap(new HashMap());
// ... put some data
synchronized (syncMap) {
for (Map.Entry<string integer> entry : syncMap.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
}</string></string>
适用场景有限:适合低并发、简单读写混合
synchronizedMap适合以下情况:
- 读多写少,且并发线程数很低(如配置缓存、单机工具类)
- 已有代码强依赖
Map接口,无法替换为ConcurrentHashMap - 不需要复合操作原子性(如“不存在则put”),或能接受额外同步块
不适合:
- 高吞吐写入场景(锁竞争严重)
- 需要批量操作原子性(如
computeIfAbsent) - 要求弱一致性读(
ConcurrentHashMap支持无锁读)
更优替代:优先考虑ConcurrentHashMap
除非有特殊兼容需求,ConcurrentHashMap通常是更现代、高效的选择:
- 分段锁(JDK 8+优化为CAS + synchronized链表/红黑树)提升并发度
- 迭代器弱一致性,不抛
ConcurrentModificationException - 提供原子复合方法:
computeIfAbsent、merge、replace等 - 默认支持null值(JDK 9+);注意key仍不可为null
迁移示例:
// 旧方式(需手动同步迭代) Map<string list>> cache = Collections.synchronizedMap(new HashMap()); // 新方式(更安全、更高效) ConcurrentHashMap<string list>> cache = new ConcurrentHashMap(); // 迭代无需外部同步 cache.forEach((k, v) -> System.out.println(k + ": " + v));</string></string>
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











