concurrenthashmap性能最优,因其读操作无锁、写操作仅锁对应桶;hashtable和collections.synchronizedmap均为全表锁,读写均阻塞。三者差异核心在于锁粒度、阻塞性及底层机制。

区分这三者的性能,关键看并发读写场景下的锁粒度、是否阻塞、以及底层机制差异。不是比“谁更快”,而是看“在什么情况下谁更合适”。
锁的范围决定并发吞吐能力
Hashtable 和 Collections.synchronizedMap 都是全表锁:任意线程调用 put、get、size 等方法,都会锁住整个 Map 实例(前者锁 this,后者锁 mutex 对象)。这意味着即使两个线程操作完全不冲突的 key(比如 key1 和 key2 哈希到不同桶),也必须排队执行。
- Hashtable:所有 public 方法都加 synchronized,包括 get —— 读操作也要等写结束
- Collections.synchronizedMap:每个方法用 synchronized(mutex) 包裹,逻辑同上,只是锁对象可自定义
- ConcurrentHashMap(JDK 8+):无全局锁。读操作几乎无锁(volatile + CAS),写操作只锁定对应 bin 的头结点(链表或红黑树根节点),多个线程可同时修改不同桶
读多写少场景下性能差距最明显
当应用以 get 为主(如缓存查询)、少量 put/remove 时:
- ConcurrentHashMap 的 get 完全无锁,仅靠 volatile 读保障可见性,响应极快
- Hashtable 和 synchronizedMap 的每次 get 都要获取和释放锁,带来上下文切换与竞争开销
- 实测中,100 线程并发读 10 万次,ConcurrentHashMap 耗时通常不足前两者的 1/5
写操作的扩展性差异显著
高并发写入(如日志聚合、计数器更新)时:
- Hashtable/synchronizedMap:所有写线程串行化,吞吐量随线程数增加几乎不升反降(锁争用加剧)
- ConcurrentHashMap:默认有 16 个并发段(由 CPU 核心数与初始化容量共同影响),16 个线程可真正并行写入不同分段;扩容也支持多线程协作,避免长时间停顿
- 它还大量使用 CAS(如 sizeCtl、baseCount)减少锁依赖,put 失败会重试而非直接阻塞
内存与 GC 开销也不容忽视
ConcurrentHashMap 在 JDK 8 后摒弃了 Segment,改用 Node 数组 + CAS + synchronized 协作,结构更轻量:
- Hashtable/synchronizedMap 封装简单,但锁竞争导致更多线程挂起/唤醒,间接增加 GC 压力(如 ThreadLocal 清理延迟)
- ConcurrentHashMap 的链表转红黑树阈值(TREEIFY_THRESHOLD=8)有效降低长链遍历成本,对热点 key 更友好
- 其 size() 方法通过 baseCount + CounterCell 数组累加实现,避免锁整个 map 统计,适合高频监控场景










