hashtable因方法级全局锁导致低效:所有操作竞争同一把锁,即使键互不冲突也需串行执行,引发高阻塞、读写无法并发、扩容时全表阻塞;concurrenthashmap则通过分段锁(jdk7)或桶级锁+无锁读(jdk8)大幅降低锁粒度,支持高并发。

Hashtable 的方法级锁机制怎么体现低效
Hashtable 所有公开方法(put、get、remove、size 等)都用 synchronized 修饰,意味着每次调用都会尝试获取同一个对象锁。哪怕两个线程操作完全不冲突的键(比如 key1 和 key2 落在不同桶里),也必须排队执行。
这种“一把锁管全局”的设计导致:
- 高并发下线程频繁阻塞和唤醒,上下文切换开销大
- 读操作也被迫等待写操作完成,无法并发读
- 扩容时整个表不可用,所有操作挂起,响应时间毛刺明显
- 吞吐量随线程数增加几乎不升反降,实测中 16 线程下性能可能比单线程还差
ConcurrentHashMap 是如何打破这个瓶颈的
它不再依赖单一锁,而是按数据访问粒度分层控制:
- JDK 7:用 16 个 Segment(本质是小 Hashtable),每个 Segment 独立加锁,最多支持 16 个线程同时写不同段
- JDK 8:彻底去掉 Segment,直接对数组中某个桶(Node 链表/红黑树的头节点)加锁,锁范围缩到单个桶位
- get 操作全程无锁,靠 volatile 保证 table 数组和 Node 字段的可见性
- 扩容支持多线程协作迁移,新旧 table 可并存,业务请求可无缝路由
从源码结构看根本差异
Hashtable 底层是单一 Entry[] table,所有元素挤在一张表里,锁绑定在整个对象实例上:
public synchronized V put(K key, V value) { ... }ConcurrentHashMap(JDK 8+)底层是 Node[] table,关键操作只锁定具体 Node:
synchronized (f) { if (tabAt(tab, i) == f) { ... } }这种结构差异决定了:Hashtable 是「同步容器」,ConcurrentHashMap 是「并发容器」——前者把并发问题推给使用者协调,后者在数据结构层面就内建并发支持。
实战中该选哪个
除非维护 JDK 1.4 时代的遗留系统,否则不应主动选用 Hashtable:
- 需要线程安全 + 高并发 → 用 ConcurrentHashMap
- 仅需简单同步、并发不高 → Collections.synchronizedMap(new HashMap()) 更轻量
- 读远多于写 → CopyOnWriteArrayList 或缓存更合适
- 要 null 键值支持 + 单线程 → HashMap 是默认选择
真正理解它们,不是记住“Hashtable 线程安全而 HashMap 不是”,而是看清锁在哪里、为什么锁、以及锁住了什么。










