HashMap 与 ConcurrentHashMap 有什么区别?通俗易懂版

原文来自于:zha-ge.cn/java/21

HashMap 与 ConcurrentHashMap 有什么区别?通俗易懂版

引子:一次线上血案

那天下午,阳光正好,我正在悠闲地喝着咖啡,突然钉钉疯狂响起------线上系统崩了!用户疯狂投诉说页面卡死,数据丢失。

打开监控一看,CPU 使用率飙到 100%,线程全部卡在一个看似无害的 HashMap 操作上。我心里咯噔一下:完了,又是并发问题!

这让我想起了一个经典的比喻:HashMap 就像一个普通的记事本,一个人用没问题,但如果多个人同时在上面写字,那就乱套了。

探索:HashMap 的"单线程世界"

HashMap 在单线程环境下表现完美,就像一个勤劳的管家:

java 复制代码
// HashMap 的基本操作,单线程下很安全
Map<String, String> userCache = new HashMap<>();
userCache.put("user123", "张三");
String userName = userCache.get("user123");
// 一切都很美好...直到并发来了

但在多线程环境下,HashMap 就像一个没有交通规则的十字路口------车辆(线程)从四面八方涌来,撞车(数据竞争)是必然的。

我开始翻阅源码,发现 HashMap 的 resize() 方法在并发时会形成环形链表,导致 CPU 100% 的死循环。这就是著名的"HashMap 死循环"问题。

转折:踩坑瞬间

当时我天真地想:"既然有并发问题,那我加个 synchronized 不就行了?"

java 复制代码
// 我的第一次"聪明"尝试
Map<String, String> userCache = new HashMap<>();
synchronized(userCache) {
    userCache.put("user456", "李四");
}
synchronized(userCache) {
    String userName = userCache.get("user456");
}

结果发现性能直接跪了!就像在高速公路上设置红绿灯,所有车辆都得排队等待,吞吐量暴跌 80%。

更要命的是,如果忘记在某个地方加 synchronized,程序依然会出问题。这种方案就像在雷区走路,一不小心就踩雷。

解决:ConcurrentHashMap 闪亮登场

这时候,ConcurrentHashMap 就像一个经验丰富的交通管制员出现了。它采用了"分段锁"的设计理念:

  • HashMap:整个十字路口只有一个红绿灯,所有方向都得等
  • ConcurrentHashMap:把十字路口分成多个小区域,每个区域独立管理
java 复制代码
// ConcurrentHashMap:并发安全且高效
ConcurrentHashMap<String, String> userCache = new ConcurrentHashMap<>();
// 多个线程可以同时操作不同的段
userCache.put("user789", "王五");  // 线程 1
String userName = userCache.get("user123");  // 线程 2 同时执行
// 和谐共处,性能还很棒

ConcurrentHashMap 的核心优势:

特性 HashMap ConcurrentHashMap
线程安全
并发性能 N/A 优秀
空值支持
迭代器 快速失败 弱一致性

经验启示

经过这次血泪教训,我总结了几个关键点:

什么时候用 HashMap?

  • 单线程环境:性能最优,无脑选择
  • 不可变数据:初始化后不再修改
  • 局部变量:方法内部使用,天然线程安全

什么时候用 ConcurrentHashMap?

  • 多线程环境:必选项,没得商量
  • 高并发场景:读写操作频繁
  • 缓存场景:多个线程共享数据

踩坑提醒

  1. 别想着给 HashMap 加锁:性能损失巨大,还容易出错
  2. 注意 null 值:ConcurrentHashMap 不允许 null 键或值
  3. 迭代器行为 :ConcurrentHashMap 的迭代器不会抛 ConcurrentModificationException

总结

这次经历让我深刻理解了一个道理:选择合适的工具,比优化代码更重要

HashMap 就像跑车,在赛道上飞驰;ConcurrentHashMap 就像越野车,在复杂地形中稳定前行。关键是要在合适的场景选择合适的工具。

现在每当有新人问我这个问题,我都会告诉他们:单线程用 HashMap,多线程用 ConcurrentHashMap,简单粗暴,不会错!

记住,程序员的智慧不在于写出多复杂的代码,而在于选择最合适的解决方案。毕竟,生产环境的稳定性,比任何炫技都重要。

相关推荐
舒一笑2 小时前
PandaCoder 1.1.8 发布:中文开发者的智能编码助手全面升级
java·后端·intellij idea
这周也會开心2 小时前
Spring-MVC
java·spring·mvc
SimonKing2 小时前
跨域,总在发OPTIONS请求?这次终于搞懂CORS预检了
java·后端·程序员
考虑考虑3 小时前
dubbo3超时时间延长
java·后端·dubbo
boy快快长大3 小时前
【Spring AI】简单入门(一)
java·人工智能·spring
失散133 小时前
分布式专题——15 ZooKeeper特性与节点数据类型详解
java·分布式·zookeeper·云原生·架构
ThisIsMirror3 小时前
Spring的三级缓存如何解决单例Bean循环依赖
java·spring·缓存
菠菠萝宝3 小时前
【Java八股文】12-分布式面试篇
java·分布式·zookeeper·面试·seata·redisson
yk100103 小时前
Spring DefaultSingletonBeanRegistry
java·后端·spring
Metaphor6923 小时前
Java 将 PDF 转换为 HTML:高效解决方案与实践
java·经验分享·pdf·html