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

相关推荐
杉氧2 小时前
状态管理变迁史:为什么我们放弃了 Redux 选择 Zustand?
android·前端·react native
三少爷的鞋5 小时前
别再靠 Code Review 守底线:我做了一个静态分析项目 RedLine
android
y = xⁿ13 小时前
DeepSeek Harness 学习日记:关于Agent接口,工具调用的底层实现
android·java·学习
2601_9620664916 小时前
【Sql Server】Update中的From语句,以及常见更新操作方式
android·java·数据库
用户693717500138421 小时前
曾经安卓开发人手一个的 EventBus,为什么现在没人爱用了?
android·android studio
九皇叔叔1 天前
MySQL ORDER BY 性能优化详解:Using filesort、联合索引、ASC/DESC 与覆盖索引
android·adb
OriginCoding1 天前
用 AI Agent 协作完成一个 Android TOTP 应用:从需求边界到 v1.0.0
android·ai编程
杉氧1 天前
React 灵魂:深入剖析 Hooks 与副作用(Side Effects)陷阱
android·前端·react native
又见情义1 天前
RK3568 + RTL8211F 网络唤醒(WOL)功能适配全记录
android·网络·驱动开发
用户0934077735141 天前
HarmonyOS WPS Open SDK 实践:registerApp 鉴权与就绪门禁
android·typescript·harmonyos