concurrenthashmap 优于 hashtable,因其采用 cas+桶级锁实现细粒度并发控制,读操作无锁、写操作仅局部加锁,支持原子复合操作与弱一致性迭代器;而 hashtable 使用全表 synchronized 锁,高并发下严重串行化且已过时。

直接用 ConcurrentHashMap,别用 Hashtable —— 这是当前 Java(JDK 8+)多线程键值对场景下的明确实践共识。
为什么优先选 ConcurrentHashMap 而不是 Hashtable
Hashtable 虽然线程安全,但靠给每个方法加 synchronized 锁住整个对象,相当于“一人操作,全员排队”。高并发下吞吐量极低,且已标记为遗留类(继承自过时的 Dictionary)。ConcurrentHashMap 则通过更细粒度的锁机制(JDK 8 是 CAS + 桶级 synchronized)实现读操作几乎无锁、写操作只锁对应哈希桶,支持真正并行访问。
- 读多写少场景:ConcurrentHashMap 的 get() 不加锁,性能接近 HashMap
- 写竞争明显时:多个线程往不同 key 写入,互不阻塞;只有哈希冲突到同一桶才可能短暂竞争
- 扩容也支持多线程协作:JDK 8 中扩容不再是单线程瓶颈
ConcurrentHashMap 的典型安全用法
它不是“加了锁就万事大吉”,而是提供原子性更强的操作方法,避免手动同步带来的竞态风险:
-
putIfAbsent(key, value):仅当 key 不存在时插入,替代 “先 get 再 put” 的非原子逻辑 -
computeIfAbsent(key, mappingFunction):key 不存在时按函数计算并缓存结果,适合懒加载场景(如缓存数据库连接) -
compute(key, remappingFunction)或merge(key, value, remappingFunction):安全地做“读-改-写”,比如原子递增计数:map.merge("count", 1, Integer::sum) - 避免使用
containsKey() + get()或size()做条件判断——这些值可能在调用间隙就被其他线程修改
Hashtable 已不推荐使用的具体表现
即使你坚持用 Hashtable,也会遇到现实约束:
- 不能存
null键或null值,一旦传入直接抛NullPointerException,容易在运行时崩 - 迭代器是
fail-fast类型,但它的“安全失败”实际依赖内部结构快照,语义模糊且不可靠 - 初始容量为 11、扩容公式为
2 * old + 1,与主流集合不一致,增加理解与维护成本 - 没有提供任何原子复合操作(如上面提到的 compute 系列),所有复杂逻辑都得自己加
synchronized块,反而破坏其“内置安全”的意义
如果必须兼容老代码或做对比测试
可以这样初始化并验证行为差异:
ConcurrentHashMap<string integer> map = new ConcurrentHashMap();</string>Hashtable<string integer> table = new Hashtable();</string>- 注意:两者都不接受
map.put(null, 1)或table.put("k", null),会立即报错 - 多线程写入后,
ConcurrentHashMap.size()返回的是近似值(基于LongAdder思想),而Hashtable.size()是精确但需锁表获取——这本身也说明设计目标不同
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











