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环境中。

相关推荐
愤怒的代码6 小时前
深入解析 Binder 运行的状态
android·app
2501_915106327 小时前
iOS App 测试方法,通过 Xcode、Instruments、Safari Inspector、克魔(KeyMob)等工具
android·ios·小程序·uni-app·iphone·xcode·safari
游戏开发爱好者87 小时前
对 iOS IPA 文件进行深度混淆的一种实现路径
android·ios·小程序·https·uni-app·iphone·webview
成都大菠萝7 小时前
2-2-5 快速掌握Kotlin-语言的泛型函数
android
成都大菠萝7 小时前
2-2-4 快速掌握Kotlin-定义泛型类
android
掘我的金7 小时前
加载状态优化实践:如何让用户始终知道当前状态
android
成都大菠萝7 小时前
2-2-6 快速掌握Kotlin-语言的多泛型参数学习
android
掘我的金7 小时前
空状态优化实践:如何让"白屏"变成友好的提示
android
_李小白7 小时前
【Android FrameWork】第三十四天:系统设置项(Settings)与系统属性(System Properties)
android·jvm·oracle