ConcurrentHashMap 和 Hashtable 的区别详解

在 Java 集合框架中,Hashtable 和 ConcurrentHashMap 都是线程安全的 Map 实现,常被用来在多线程环境下存储键值对。很多初学者容易把它们混为一谈,认为功能一样就可以互换使用,但实际上两者在设计理念、性能、功能和适用场景上存在巨大差异。

主要区别

对比项 Hashtable ConcurrentHashMap (JDK 8) 说明与影响
出现版本 JDK 1.0(非常古老) JDK 1.5 引入,JDK 8 大幅重构 Hashtable 是遗留类,ConcurrentHashMap 是现代高并发首选
线程安全实现方式 所有公共方法都加 synchronized(对象级锁) 分段锁(JDK 7)→ CAS + synchronized(JDK 8) Hashtable 锁粒度大,并发度低;ConcurrentHashMap 锁粒度细,并发性能高
锁粒度 整个对象一把锁 JDK 8:单个桶(bucket)级别 synchronized + CAS ConcurrentHashMap 支持更高的并发读写
读操作是否加锁 是(get 也加 synchronized) 否(get 不加锁,基于 volatile 可见性) ConcurrentHashMap 的 get 操作无锁,读取性能极高
允许 null 键/值 不允许(put null 会抛 NullPointerException) 不允许键为 null,允许值为 null 两者都不允许 null 键,但 Hashtable 连值也不允许
迭代器行为 fail-fast(并发修改抛 ConcurrentModificationException) weak consistent(弱一致性),迭代器不会抛异常 ConcurrentHashMap 的迭代器能反映并发修改后的状态,更适合高并发遍历
容量扩容机制 容量为 2 倍 + 1,扩容时重新 hash 容量为 2 的幂次方,JDK 8 扩容时高效 rehash ConcurrentHashMap 扩容更高效,支持并发扩容
size()、isEmpty() 准确性 准确(加锁计算) 近似值(不加锁统计,可能有延迟) 高并发下 ConcurrentHashMap 的 size() 不保证实时精确,但性能更好
性能(高并发场景) 较低(全表锁) 极高(细粒度锁 + 无锁读) 实际生产环境中,ConcurrentHashMap 性能通常是 Hashtable 的数倍到数十倍
是否推荐继续使用 不推荐(已过时) 强烈推荐 官方建议用 ConcurrentHashMap 替代 Hashtable

两者都不支持 null 键,如果必须用 null,考虑 Collections.synchronizedMap(new HashMap<>())(但性能差)

深入细节说明

  • 锁机制差异(最核心区别)
    • Hashtable:几乎所有公共方法(put、get、remove、size 等)头部都直接加了 synchronized,相当于对整个 Hashtable 对象加锁。任意时刻只能有一个线程操作,严重限制并发能力。
    • ConcurrentHashMap(JDK 8):
      • get 操作完全无锁,依赖 Node 字段的 volatile 语义保证可见性。
      • put/remove 操作只锁住对应的桶(bucket),其他桶可以并发操作。
      • 使用 CAS(Compare-And-Swap)无锁操作 + 必要时 synchronized 锁单个桶。
      • 扩容时支持多线程协同转移数据(transfer),进一步提升性能。
  • 迭代器特性
    • Hashtable 的迭代器是 fail-fast 类型的,一经发现并发修改就立刻抛异常。
    • ConcurrentHashMap 的迭代器是弱一致性(weakly consistent),遍历过程中如果有其他线程修改,迭代器会尽量反映最新的数据,不会抛异常。这在高并发日志、监控统计等场景非常有用。
  • null 值处理
    • Hashtable:键和值都不能为 null,put(null) 会直接抛 NullPointerException。
    • ConcurrentHashMap:键不能为 null(会抛 NullPointerException),但值可以为 null。
  • 历史演进
    • JDK 7 的 ConcurrentHashMap 使用 Segment 分段锁(默认 16 段)。
    • JDK 8 彻底重构,放弃 Segment,改为更细粒度的桶级锁 + 红黑树优化(链表 >8 转树),性能和内存利用率都大幅提升。

代码对比示例

java 复制代码
// Hashtable 示例(所有操作都加锁)
Hashtable<String, Integer> hashtable = new Hashtable<>();
hashtable.put("key", 1);
Integer value = hashtable.get("key");

// ConcurrentHashMap 示例(get 无锁,put 只锁对应槽)
ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
concurrentMap.put("key", 1);
Integer value = concurrentMap.get("key");  // 高并发下极快

总结

Hashtable 是 Java 早期粗粒度同步的产物,已被时代淘汰;ConcurrentHashMap 是现代高并发设计的典范,拥有更高的吞吐量、更细的锁粒度、更友好的迭代器和更好的扩容机制。

生产环境中的最佳实践永远优先使用 ConcurrentHashMap,彻底抛弃 Hashtable

相关推荐
凛_Lin~~2 小时前
安卓 面试八股文整理(原理与性能篇)
android·java·面试·安卓
阿猿收手吧!2 小时前
【C++】brpc与grpc对比
开发语言·c++
weixin_436525072 小时前
NestJS-TypeORM QueryBuilder 常用 SQL 写法
java·数据库·sql
oioihoii2 小时前
C++虚函数表与多重继承内存布局深度剖析
java·jvm·c++
会员果汁2 小时前
算法-拓扑排序-C
c语言·开发语言·算法
wangchen_02 小时前
深入理解 C/C++ 强制类型转换:从“暴力”到“优雅”
java·开发语言·jvm
Wang15302 小时前
Java三大核心热点专题笔记
java
lly2024062 小时前
CSS 颜色
开发语言
潲爺3 小时前
《Java 8-21 高频特性实战(上):5 个场景解决 50% 开发问题(附可运行代码)》
java·开发语言·笔记·学习