hashtable 能直接多线程使用因其所有 public 方法均加 synchronized 锁,hashmap 无同步机制,多线程并发 put 可能导致扩容死循环或数据丢失,且其迭代器 fail-fast,结构变更即抛 concurrentmodificationexception。

Hashtable 和 HashMap 为什么一个能直接多线程用,一个不行?
因为 Hashtable 每个 public 方法(比如 put、get、remove)都加了 synchronized,相当于整张表一把大锁;而 HashMap 完全没这层保护,多个线程同时 put 可能触发扩容+链表循环,JDK 7 甚至会卡死线程。
- 不是“偶尔出错”,是结构性风险:并发
put可能导致get死循环或数据丢失 - 别指望“我只读不写”就安全——
HashMap的迭代器(Iterator)是 fail-fast 的,遍历时其他线程一改结构,立刻抛ConcurrentModificationException - 真要线程安全,
ConcurrentHashMap是现代首选,它用分段锁/CAS,读操作完全无锁,性能远超Hashtable
HashMap 允许 null 键值,Hashtable 一放就崩,原因在哪?
Hashtable 在 put 开头就显式判空:if (key == null || value == null) throw new NullPointerException();而 HashMap 对 null 键做了特殊哈希处理(固定放在数组第 0 个桶),null 值则完全放行。
-
map.put(null, "ok")在HashMap中合法,在Hashtable中直接NullPointerException - 注意陷阱:
map.get(null)返回null时,你无法区分是“没这个键”还是“这个键对应值就是 null”——得用containsKey(null)单独判断 - 如果业务逻辑依赖
null作为有效语义(比如“未设置状态”),Hashtable直接出局
现在还有必要选 Hashtable 吗?
几乎没有。它被设计出来时连 Collection 框架都没有,继承自已废弃的 Dictionary 类,连 forEach、stream 都不支持。
- 初始容量是 11,扩容是
old * 2 + 1,和HashMap的 16→32→64 规则不一致,纯属历史包袱 - 迭代器不是 fail-fast,改了结构也不报错,反而更难定位并发问题
- 如果你在维护 JDK 1.0–1.4 的老系统,才可能见到它;新项目里写
Hashtable,会被同事当反模式案例讲
该用哪个 Map?一句话决策树
单线程场景?无脑用 HashMap。需要线程安全?优先 ConcurrentHashMap,实在要锁整个 map 才考虑 Collections.synchronizedMap(new HashMap()) ——但别碰 Hashtable。
- 想存
null键?只能选HashMap或ConcurrentHashMap(它也允许null值,但禁止null键) - 用
ConcurrentHashMap时注意:computeIfAbsent等方法内部有锁,回调函数里不要再同步调用自身,容易死锁 - 最常被忽略的一点:
Hashtable的elements()和keys()返回的是Enumeration,不是Iterator,没法用增强 for 循环,连 lambda 都接不上










