Collections.synchronizedMap()与ConcurrentHashMap的区别笔记

Collections.synchronizedMap()ConcurrentHashMap 都是Java中用于线程安全的Map实现,但它们在设计理念、性能和特性上有显著区别:

1. 锁的粒度

Collections.synchronizedMap()

  • 全表锁:对整个Map对象加锁
  • 任何线程访问任何方法时都会锁定整个Map
  • 读操作和写操作都会阻塞其他所有操作

ConcurrentHashMap

  • 分段锁 (Java 7)或 CAS + synchronized(Java 8)
  • 只锁定部分数据(桶或节点)
  • 允许多个线程同时进行读操作
  • 允许不同线程同时修改不同的段/节点

2. 并发性能对比

java 复制代码
// synchronizedMap - 低并发性能
Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());

// ConcurrentHashMap - 高并发性能
ConcurrentHashMap<String, String> concurrentMap = new ConcurrentHashMap<>();
场景 synchronizedMap ConcurrentHashMap
读多写少 差(所有读操作串行) 优秀(读操作无锁)
写多读少 差(所有写操作串行) 良好(部分写可并行)
高并发 性能急剧下降 性能保持相对稳定

3. 迭代器行为

synchronizedMap

  • 强一致性迭代器
  • 迭代过程中会持有锁,阻止其他线程修改
  • 迭代时看到的是某个时间点的快照
java 复制代码
Map<String, String> syncMap = Collections.synchronizedMap(new HashMap<>());
// 迭代时需要手动同步
synchronized(syncMap) {
    for(String key : syncMap.keySet()) {
        // 线程安全
    }
}

ConcurrentHashMap

  • 弱一致性迭代器
  • 迭代时不需要锁,不阻止其他线程修改
  • 可能反映迭代过程中的修改,也可能不反映
java 复制代码
ConcurrentHashMap<String, String> concurrentMap = new ConcurrentHashMap<>();
// 迭代不需要额外同步
for(String key : concurrentMap.keySet()) {
    // 可能看到其他线程的修改
}

4. 空值支持

特性 synchronizedMap ConcurrentHashMap
key为null 允许(取决于底层Map) 不允许
value为null 允许(取决于底层Map) 不允许

5. 内存可见性保证

synchronizedMap

  • 通过synchronized关键字保证可见性
  • 遵循synchronized的happens-before规则

ConcurrentHashMap

  • 使用volatile变量保证可见性
  • 遵循JMM(Java内存模型)的happens-before规则

6. 原子操作支持

synchronizedMap

  • 无内置原子操作
  • 需要手动同步实现复合操作
java 复制代码
// 手动实现putIfAbsent
synchronized(syncMap) {
    if(!syncMap.containsKey(key)) {
        syncMap.put(key, value);
    }
}

ConcurrentHashMap

  • 提供丰富的原子操作API
java 复制代码
// 原子操作,无需外部同步
concurrentMap.putIfAbsent(key, value);
concurrentMap.compute(key, (k, v) -> v == null ? 1 : v + 1);
concurrentMap.merge(key, 1, Integer::sum);

7. 推荐使用场景

使用 Collections.synchronizedMap() 当:

  1. 并发量非常低
  2. 需要保持与现有代码的兼容性
  3. 需要允许null值
  4. 对性能要求不高的小规模应用

使用 ConcurrentHashMap 当:

  1. 高并发环境
  2. 读多写少的场景
  3. 需要更好的可伸缩性
  4. 需要使用原子操作
  5. 现代Java应用(Java 5+)

8. 代码示例对比

java 复制代码
// 方式1: synchronizedMap
public class SyncMapExample {
    private Map<String, Integer> map = 
        Collections.synchronizedMap(new HashMap<>());
    
    public void increment(String key) {
        synchronized(map) {  // 需要额外同步
            map.put(key, map.getOrDefault(key, 0) + 1);
        }
    }
}

// 方式2: ConcurrentHashMap
public class ConcurrentMapExample {
    private ConcurrentHashMap<String, Integer> map = 
        new ConcurrentHashMap<>();
    
    public void increment(String key) {
        map.compute(key, (k, v) -> v == null ? 1 : v + 1);
        // 或者使用原子操作
        // map.merge(key, 1, Integer::sum);
    }
}

总结表格

特性 Collections.synchronizedMap() ConcurrentHashMap
锁机制 全表锁 分段锁/CAS+synchronized
并发性能
迭代器 强一致性,需要锁 弱一致性,无需锁
空值 允许 不允许
原子操作 需要手动同步 内置支持
内存开销 较低 较高(分段结构)
适用场景 低并发、简单场景 高并发、复杂场景

建议 :在现代Java并发编程中,优先考虑使用ConcurrentHashMap,除非有特殊需求(如需要null值)或运行在低版本Java环境中。

相关推荐
_李小白10 分钟前
【Android 美颜相机】第三天:初识GPUImageView
android·数码相机
行稳方能走远13 分钟前
Android java 学习笔记3
android·java
Larry_Yanan14 分钟前
Qt安卓开发(二)摄像头打开
android·开发语言·数据库·c++·qt·ui
Sammyyyyy14 分钟前
PHP 8.6 新特性预览,更简洁的语法与更严谨的类型控制
android·php·android studio
撩得Android一次心动16 分钟前
Android Lifecycle 全面解析:掌握生命周期管理的艺术(1)
android·java·kotlin·lifecycle
毕设源码-朱学姐42 分钟前
【开题答辩全过程】以 基于安卓的家校交流平台为例,包含答辩的问题和答案
android
帅得不敢出门1 小时前
MTK Android DuraSpeed优化机制导致app应用收不到广播分析解决
android
2501_915106321 小时前
HBuilderX 项目上架 iOS app上架 App Store 的关键流程
android·ios·小程序·https·uni-app·iphone·webview