【面试高频】 HashMap, HashTable,ConcurrentHashMap 之间的区别

目录

1.核心特性区别

2.锁机制与并发原理区别

[2.1 HashMap(无锁)](#2.1 HashMap(无锁))

[2.2 HashTable(全局独占锁)](#2.2 HashTable(全局独占锁))

[2.3 ConcurrentHashMap:细粒度锁 + 无锁并发(性能最优)](#2.3 ConcurrentHashMap:细粒度锁 + 无锁并发(性能最优))

3.疑惑解答

[1. 为什么 HashMap 允许 null 键值,而另外两者不允许?](#1. 为什么 HashMap 允许 null 键值,而另外两者不允许?)

[2. 为什么不推荐用 HashTable?](#2. 为什么不推荐用 HashTable?)

4.常见面试题


我们在学习Java的过程中,通常遇到算法类的问题时会用到HashMap 的数据结构,在单线程的情况下,HashMap确实是最优选;但如果是多线程,那么HashMap就会造成一定的线程安全问题,所以为了解决线程安全问题,引入HashTableConcurrentHashMap就可以更好的应对线程安全问题。

1.核心特性区别

  • HashMap:非线程安全 ,允许key值为null(但只能1个key为null),单线程下性能高,适用于单线程的所有情况,适合日常开发。
  • **HashTable:**线程安全 ,不允许key值为null,锁密度极大,性能极低,已过时废弃
  • ConcurrentHashMap:线程安全 ,性能优异,不允许key值为null,极大降低了锁冲突概率,专为多线程高并发场景设计,代替HashTable并发容器。

2.锁机制与并发原理区别

三者最大的差异本质是线程安全的实现方式不同,直接决定性能高低:

2.1 HashMap(无锁)

全程没有任何锁机制,单线程高效读写。但是在多线程环境下,会出现数据覆盖、扩容死循环、元素丢失 等线程安全问题,记住,HashMap绝对不能直接用于多线程场景

2.2 HashTable(全局独占锁)

HashTable 所有增删改查方法都直接加synchronized全局对象锁,同一时间只有一个线程能操作哈希表。无论读写、操作哪个数据,都会阻塞其他所有线程,锁粒度极大、并发度为 1,高并发场景下竞争激烈、性能极差。

从上图的源码中就可以看出HashTable就是给所有的方法都暴力的加上synchronized关键字,物极必反,线程安全性确实保证了,但是大大提高了锁冲突的概率,所有效率极低。

2.3 ConcurrentHashMap:细粒度锁 + 无锁并发(性能最优)

专门为高并发优化而设计的,摒弃 HashTable 的全局锁,实现读写分离、高并发并行:

**✅JDK1.7( 分段锁):**将容器分为 16 个独立 Segment,多线程可同时操作不同分段,互不阻塞,并发度提升 16 倍

✅JDK1.8( 桶级锁 + CAS): 读操作无锁(依靠 volatile 保证可见性),写操作仅锁定当前操作的数组桶,其他桶可正常并发读写;结合 CAS 无锁操作,极大降低锁竞争

✅迭代器为弱一致性,遍历过程中数据修改不会抛出并发修改异常,适配并发遍历场景

3.疑惑解答

1. 为什么 HashMap 允许 null 键值,而另外两者不允许?

  • **HashMap:**单线程场景,null 键会固定存储在数组下标 0 位置,开发者可清晰区分 null 键和普通键,无并发歧义。

  • **HashTable/ConcurrentHashMap:**多线程场景下,无法区分「key 为 null」和「key 不存在」两种情况,会导致业务逻辑歧义;同时 null 操作会触发空指针异常,因此直接禁止 null 键值,规避并发隐患。

2. 为什么不推荐用 HashTable?

  • HashTable 存在致命缺陷:全局锁导致并发性能极差,无红黑树优化、结构老旧、不支持 null 值,且官方已标记过时。多线程场景优先使用 ConcurrentHashMap,单线程优先 HashMap,完全可以替代 HashTable 所有使用场景。

4.常见面试题

  • 💬ConcurrentHashMap 的读是否要加锁,为什么?

答: 读操作没有加锁,目的是为了进一步降低锁冲突的概率。为了保证读到刚修改的数据,搭配了 volatile 关键字。

  • 💬介绍下 ConcurrentHashMap 的锁分段技术?

**答:**这个是 Java1.7 中采取的技术,Java1.8 中已经不再使用了。简单的说就是把若干个哈希桶分成一个 "段"(Segment),针对每个段分别加锁。 目的也是为了降低锁竞争的概率。当两个线程访问的数据恰好在同一个段上的时候,才触发锁竞争。

  • 💬 ConcurrentHashMap 在 jdk1.8 做了哪些优化?

答: 取消了分段锁,直接给每个哈希桶 (每个链表) 分配了一个锁 (就是以每个链表的头结点对象作为锁对象)。 将原来数组 + 链表的实现方式改进成数组 + 链表 / 红黑树的方式。当链表较长的时候 (大于等于 8 个元素) 就转换成红黑树。

  • 💬 Hashtable 和 HashMap、ConcurrentHashMap 之间的区别?

答:

  1. HashMap: 线程不安全,key 允许为 null
  2. Hashtable: 线程安全,使用 synchronized 锁 Hashtable 对象,效率较低,key 不允许为 null。
  3. ConcurrentHashMap: 线程安全,使用 synchronized 锁每个链表头结点,锁冲突概率低,充分利用 CAS 机制,优化了扩容方式,key 不允许为 null。

总结下来就是,单线程选HashMap,多线程选ConcurrentHashMap,HashTable性能最低一般不采用。觉得有帮助的小伙伴不妨点个关注再走叭~

相关推荐
城管不管2 小时前
第八次面试2026.8.10
面试·职场和发展
JieE2122 小时前
单例模式:为什么大模型只加载一次就够了?
面试
windliang2 小时前
Claude Code 源码分析(八):Memory 如何被写入、整理与按需召回
前端·算法·面试
lingran__3 小时前
C++ 高阶数据结构:红黑树万字详解|完整原理推导 + 插入实现 + 完整性校验【STL 底层】
数据结构·c++·面试·红黑树·二叉搜索树·平衡二叉树·stl底层
ssshooter3 小时前
为什么明明只有一个 12px 的小元素,父容器却有 63px 高?
前端·javascript·面试
淡海水3 小时前
15-07-YooAsset面试篇-Unity性能优化与内存管理
java·unity·面试·性能优化·c#·游戏引擎
.柒宇.4 小时前
运维常见面试题_02_数据库
运维·数据库·sql·面试
kyriewen15 小时前
面试官问"这段逻辑为什么这样写"——我才发现,这半年我写的代码,我自己都解释不了
前端·javascript·面试
大龄秃头程序员18 小时前
深入梳理 UIView touch 事件全链路原理与实战
面试