concurrenthashmap比hashtable快,因其采用分段锁(jdk1.7)或cas+桶级synchronized(jdk1.8),避免全局锁,读操作无锁且靠volatile保证可见性,显著降低线程阻塞。

直接用 ConcurrentHashMap 替代 Hashtable,核心不是“换类名”,而是利用它更细粒度的锁机制和无锁读特性,在多线程读多写少场景下显著减少线程阻塞。它不靠全局锁,而是只在真正冲突的位置加锁,读操作几乎零开销。
为什么 ConcurrentHashMap 比 Hashtable 快
Hashtable 所有方法都用 synchronized 修饰,相当于整张表一把大锁——哪怕两个线程操作完全不同的 key,也得排队。而 ConcurrentHashMap:
- JDK 1.7:把哈希表拆成 16 个 Segment(默认),每个 Segment 独立加锁,最多支持 16 个线程同时写不同段
- JDK 1.8:彻底去掉 Segment,改用 CAS + 对单个桶(数组槽位)加
synchronized锁,锁粒度缩小到链表或红黑树的头节点 - 所有
get()操作全程无锁,靠volatile字段保证可见性,读性能接近HashMap
替换时的关键写法差异
接口基本兼容,但要注意几个关键点:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 构造时不传初始容量和负载因子,而是传
concurrencyLevel(JDK 1.7)或忽略(JDK 1.8 已废弃该参数),实际并发能力由内部自动调节 - 避免用
size()做条件判断——它是估算值,高并发下可能不准;需要精确计数用mappingCount()(返回 long,更可靠) - 不要用
contains()或elements()这类已废弃方法;改用containsKey()、containsValue()或流式处理 - 迭代器弱一致性:遍历时可能看不到最新 put 的元素,也不会抛
ConcurrentModificationException,这是设计使然,不是 bug
典型高性能用法示例
别只做简单 put/get,发挥它的原子能力:
- 计数统计:
map.compute(key, (k, v) -> v == null ? 1 : v + 1)或更简洁的map.merge(key, 1, Integer::sum) - 条件写入:
map.putIfAbsent(key, value)替代先get()再put()的双重检查 - 安全替换:
map.replace(key, oldValue, newValue)保证仅当旧值匹配时才更新 - 批量初始化:用
ConcurrentHashMap.newKeySet()创建线程安全的 key 集合,或用computeIfAbsent()实现懒加载缓存
什么情况下仍要小心
它快,但不是万能的:
- 如果业务要求强一致性(比如金融对账),不能依赖
get()立即看到put()结果,要考虑额外同步或使用其他机制 - 全量遍历(如
entrySet().forEach())期间,其他线程仍可修改,结果是某一时刻的快照,不是事务性视图 - 批量操作如
putAll()不是原子的,内部逐个调用put(),需自行加锁控制范围 - 单线程场景下,
ConcurrentHashMap比HashMap多出 CAS 和 volatile 开销,纯属浪费
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










