登录
首页 >  文章 >  java教程

Hashtable与HashMap线程安全对比

时间:2026-04-15 13:17:31 115浏览 收藏

Hashtable 与 HashMap 的核心差异在于线程安全机制和 null 值处理:Hashtable 通过全方法 synchronized 实现“开箱即用”的线程安全,但代价是性能低下、不支持 null 键值、迭代器非 fail-fast 且 API 过时;HashMap 则完全无同步,高并发下易引发死循环或数据丢失,虽允许 null 键值但需警惕歧义判断,而现代替代方案 ConcurrentHashMap 以分段锁/CAS 实现高性能、细粒度并发控制,并兼容主流集合操作——新项目中 Hashtable 已成历史遗迹,应坚决摒弃,优先选择 HashMap(单线程)或 ConcurrentHashMap(多线程),并谨慎规避其并发陷阱与 null 语义误区。

什么是Hashtable_与HashMap在线程安全与Null值处理上的对比

Hashtable 和 HashMap 为什么一个能直接多线程用,一个不行?

因为 Hashtable 每个 public 方法(比如 putgetremove)都加了 synchronized,相当于整张表一把大锁;而 HashMap 完全没这层保护,多个线程同时 put 可能触发扩容+链表循环,JDK 7 甚至会卡死线程。

  • 不是“偶尔出错”,是结构性风险:并发 put 可能导致 get 死循环或数据丢失
  • 别指望“我只读不写”就安全——HashMap 的迭代器(Iterator)是 fail-fast 的,遍历时其他线程一改结构,立刻抛 ConcurrentModificationException
  • 真要线程安全,ConcurrentHashMap 是现代首选,它用分段锁/CAS,读操作完全无锁,性能远超 Hashtable

HashMap 允许 null 键值,Hashtable 一放就崩,原因在哪?

Hashtableput 开头就显式判空:if (key == null || value == null) throw new NullPointerException();而 HashMapnull 键做了特殊哈希处理(固定放在数组第 0 个桶),null 值则完全放行。

  • map.put(null, "ok")HashMap 中合法,在 Hashtable 中直接 NullPointerException
  • 注意陷阱:map.get(null) 返回 null 时,你无法区分是“没这个键”还是“这个键对应值就是 null”——得用 containsKey(null) 单独判断
  • 如果业务逻辑依赖 null 作为有效语义(比如“未设置状态”),Hashtable 直接出局

现在还有必要选 Hashtable 吗?

几乎没有。它被设计出来时连 Collection 框架都没有,继承自已废弃的 Dictionary 类,连 forEachstream 都不支持。

  • 初始容量是 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 键?只能选 HashMapConcurrentHashMap(它也允许 null 值,但禁止 null 键)
  • ConcurrentHashMap 时注意:computeIfAbsent 等方法内部有锁,回调函数里不要再同步调用自身,容易死锁
  • 最常被忽略的一点:Hashtableelements()keys() 返回的是 Enumeration,不是 Iterator,没法用增强 for 循环,连 lambda 都接不上

到这里,我们也就讲完了《Hashtable与HashMap线程安全对比》的内容了。个人认为,基础知识的学习和巩固,是为了更好的将其运用到项目中,欢迎关注golang学习网公众号,带你了解更多关于的知识点!

资料下载
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>