hashtable 未被废弃但不推荐使用,因全表同步锁导致性能差、不支持null、api陈旧,官方建议改用concurrenthashmap。

Hashtable 被废弃了吗?不是废弃,是不推荐用
Hashtable 没有被 JVM 标记为 @Deprecated,但它在 Java 5 之后的官方文档里被明确建议“请使用 ConcurrentHashMap 替代”。这不是语法层面的废弃,而是工程实践中的淘汰——它太重、太老、API 设计反直觉,且线程安全代价远高于现代替代方案。
常见错误现象:Hashtable 的 put() 和 get() 全是 synchronized 方法,导致整个表锁住;多线程下吞吐量常比 HashMap + 手动同步还低。
- 所有方法都加了
synchronized,哪怕只是读一个不存在的 key,也要抢锁 - 不支持
null键或值,一塞就抛NullPointerException,而这个异常甚至不是在构造时抛,是在第一次put(null, ...)时才暴露 - 继承自古老的
Dictionary类(JDK 1.0),和现代集合框架(Map接口)割裂,比如没有forEach()、computeIfAbsent()等函数式方法
HashMap 线程安全吗?完全不安全,但别急着加 synchronized
HashMap 在多线程写入场景下会直接崩溃:可能死循环(JDK 7)、扩容时数据丢失(JDK 8+),错误信息通常是 ConcurrentModificationException 或线程卡死在 get() 里打转。
使用场景判断很关键:如果你只是“读多写少”,且写操作能串行化(比如仅由单个调度线程触发),那 HashMap + 外部锁或 Collections.synchronizedMap() 就够用;但一旦写操作分散在多个线程,或者要求高并发读写,就必须换。
立即学习“Java免费学习笔记(深入)”;
-
Collections.synchronizedMap(new HashMap())只是对每个方法加了synchronized(this),本质还是全表锁,性能和Hashtable差不多 - 不要自己用
synchronized(map) { map.put(...); }包裹操作——这解决不了迭代时的并发问题(Iterator仍可能失效) - JDK 8 的
HashMap扩容时用的是分段迁移,但前提是没其他线程同时写;并发写依然会破坏结构
真正该用什么?ConcurrentHashMap 是默认答案
ConcurrentHashMap 不是“线程安全版 HashMap”,它是为并发设计的全新实现:JDK 7 用分段锁(Segment),JDK 8 改用 synchronized + CAS 控制桶节点,读操作完全无锁,写操作只锁单个桶。
参数差异明显:ConcurrentHashMap 构造时的 concurrencyLevel(JDK 7)或 initialCapacity(JDK 8+)只影响初始分桶数,不决定锁粒度——锁是动态的,按需落在具体链表或红黑树头节点上。
- 允许
null值(但不允许null键),行为和HashMap一致,迁移成本低 - 迭代器弱一致性:遍历时其他线程修改不会抛异常,但可能看不到最新更新,也不会出现
ConcurrentModificationException - 批量操作如
computeIfAbsent()、merge()是原子的,不用额外同步——这是Hashtable和synchronizedMap完全做不到的
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.computeIfAbsent("key", k -> slowInit(k)); // 整个“查 + 初始化”原子执行
还有哪些坑容易被忽略?
最常被跳过的点:ConcurrentHashMap 的 size() 不是 O(1),而是要遍历所有段统计,高并发下可能返回过期值;真要精确计数,得用 mappingCount()(返回 long,更准,但仍是近似)。
另一个隐形陷阱:把 ConcurrentHashMap 当成“万能线程安全容器”用,结果在外部做复合操作时翻车。比如“检查 key 是否存在,再 put”,这俩步骤之间仍有竞态——必须用 putIfAbsent() 或 computeIfAbsent() 替代。
-
containsKey() + put()组合永远是错的,无论用哪个 Map 实现 -
ConcurrentHashMap不保证全局顺序,keySet().toArray()返回的数组顺序不定,不能依赖 - 如果业务强依赖阻塞等待(比如某个 key 初始化完成才继续),
ConcurrentHashMap不提供这种能力,得配合CountDownLatch或CompletableFuture
事情说清了就结束











