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

相关推荐
andr_gale28 分钟前
04_rc文件语法规则
android·framework·aosp
祖国的好青年1 小时前
VS Code 搭建 React Native 开发环境(Windows 实战指南)
android·windows·react native·react.js
黄林晴2 小时前
警惕!AGP 9.2 别只改版本号,R8 规则与构建链路全线收紧
android·gradle
小米渣的逆袭2 小时前
Android ADB 完全使用指南
android·adb
儿歌八万首2 小时前
Jetpack Compose Canvas 进阶:结合 animateFloatAsState 让自定义图形动起来
android·动画·compose
zhangphil3 小时前
Android Page 3 Flow读sql数据库媒体文件,Kotlin
android·kotlin
神探小白牙4 小时前
echarts,3d堆叠图
android·3d·echarts
李白的天不白4 小时前
如何项目发布到github上
android·vue.js
summerkissyou19874 小时前
Android-RTC、NTP 和 System Time(系统时间)
android