HashMap, HashTable, ConcurrentHashMap 之间的区别

简介: HashMap, HashTable, ConcurrentHashMap 之间的区别

关于线程安全


我们知道 HashMap 是线程不安全的.

如果要在多线程环境下使用哈希表, 则可以使用:HashTable ConcurrentHashMap.

HashTable 是给关键方法加上锁, 给方法加锁就相当于针对 this 加锁.


HashTable 和 ConcurrentHashMap 的区别


1. 加锁粒度不同(最关键 最核心的区别!!!)


什么是锁粒度呢?


就是 synchronized 代码块所含代码的多少.(代码越多 粒度越粗, 代码越少 粒度越细)


引申: 锁粗化


写代码时, 一般情况下, 我们希望锁的粒度小一点更好.(串行执行的代码少, 并发执行的代码多)

如果某个场景要频繁加锁和解锁, 此时编译器就可能把这几个加锁解锁操作优化成一个更粗粒度的锁.(每次加锁解锁都会有开销, 特别是释放完锁后重新加锁, 这时就要重新进行锁竞争)


为什么加锁粒度不同呢?

HashTable 是针对整个哈希表加锁, 任何的增删改查操作都会触发加锁, 也就都可能触发锁竞争.


我们知道哈希表是一个数组, 每个数组元素都是一条链表, 当链表达到一定长度后, 链表就会被替换为红黑树.

e30f45a2b004462bad24cfb32a6e7364.png


这样加锁固然能使其线程安全, 但它的效率就大大降低了.


假设一个这样的场景, 我们同时对每条链表都进行一次修改, 显然这些修改不会相互影响, 但会引发锁冲突, 导致阻塞等待, 使代码执行效率大大降低.


竟然如此, 那我们可以多加几把锁, 将每条链表都加上不同的锁, 这样对不同的链表进行操作就不会产生阻塞等待了, 大大提到代码效率. 这便是ConcurrentHashMap 的加锁方式.

438e64b61aee429998e6d435f23caabc.png


它是如何实现每个链表都加上不同的锁呢?

针对每个操作, 我们都在获取到头节点后, 将链表的头节点放入 synchronized 中, 因为每个头节点都不同, 所以每把锁的锁对象都不同, 极大的降低了锁冲突.


给每个链表加锁是从 Java8 开始的, 在 Java1.7 之前 ConcurrentHashMap 是使用 “分段锁”, 什么是分段锁呢? 其实就是好几个链表共用一把锁.(“分段锁” 效率不高, 代码写起来也麻烦)


2. ConcurrentHashMap 利用了 CAS 机制 (无锁编程)


有些操作可以直接使用 CAS 完成, 比如获取/更新元素个数.

CAS 也能保证线程安全, 往往比锁更高效, 但是适用范围没有锁广泛.


3. 优化了扩容策略


对于 HashTable, 如果元素太多了, 就会涉及到扩容, 根据负载因子来决定是否扩容, 扩容就要重新申请一段内存空间, 把数组元素从旧哈希表上删除, 添加到新哈希表上.

如果哈希表上元素有很多 都上亿了, 那么搬运一次的成本将非常高, 导致 put 操作将非常卡顿.


对于 ConcurrentHashMap 它的搬运策略是 化整为零.

当 put 触发了触发扩容, 此时就会申请一块更大的内存空间, 但并不会一次就把元素搬运完, 而是搬运一部分(每次对哈希表进行操作时, 都搬运一小部分).此时就会有两个哈希表, 这时添加新元素时, 就是往新表插入;

删除, 查找, 修改元素时 就是对新旧两个表进行查找, 再进行操作.

(虽然相比 HashTable 多了浪费了一块空间, 但为了效率还是值得的)


相关文章
|
1月前
|
安全
HashTable与HashMap的区别
(1)HashTable的每个方法都用synchronized修饰,因此是线程安全的,但同时读写效率很低 (2)HashTable的Key不允许为null (3)HashTable只对key进行一次hash,HashMap进行了两次Hash (4)HashTable底层使用的数组加链表HashTable与HashMap的区别
23 2
|
2月前
|
存储 开发者
HashMap和Hashtable的key和value可以为null吗,ConcurrentHashMap呢
HashMap的key可以为null,value也可以为null;Hashtable的key不允许为null,value也不能为null;ConcurrentHashMap的key不允许为null
|
1天前
|
存储 安全 Java
如何优雅地回答HashSet与HashMap的区别?看这里!
哈喽,大家好!我是小米,29岁程序员。本文聚焦Java开发中经典的面试题——HashSet和HashMap的区别。HashSet基于HashMap实现,存储唯一值;HashMap存储键值对。两者在数据结构、使用场景、操作方法等方面有显著差异。HashSet无序且依赖元素的hashCode和equals方法保证唯一性,而HashMap需注意线程安全问题。掌握这些知识点,助你轻松应对面试。更多技术干货,欢迎关注我的微信公众号“软件求生”。
16 4
|
2月前
|
Java
Java基础之 JDK8 HashMap 源码分析(中间写出与JDK7的区别)
这篇文章详细分析了Java中HashMap的源码,包括JDK8与JDK7的区别、构造函数、put和get方法的实现,以及位运算法的应用,并讨论了JDK8中的优化,如链表转红黑树的阈值和扩容机制。
39 1
|
2月前
|
存储 缓存 Java
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
【用Java学习数据结构系列】HashMap与TreeMap的区别,以及Map与Set的关系
44 1
|
4月前
|
存储 Java 开发者
HashMap线程安全问题大揭秘:ConcurrentHashMap、自定义同步,一文让你彻底解锁!
【8月更文挑战第24天】HashMap是Java集合框架中不可或缺的一部分,以其高效的键值对存储和快速访问能力广受开发者欢迎。本文深入探讨了HashMap在JDK 1.8后的底层结构——数组+链表+红黑树混合模式,这种设计既利用了数组的快速定位优势,又通过链表和红黑树有效解决了哈希冲突问题。数组作为基石,每个元素包含一个Node节点,通过next指针形成链表;当链表长度过长时,采用红黑树进行优化,显著提升性能。此外,还介绍了HashMap的扩容机制,确保即使在数据量增大时也能保持高效运作。通过示例代码展示如何使用HashMap进行基本操作,帮助理解其实现原理及应用场景。
68 1
|
4月前
|
开发者 C# UED
WPF与多媒体:解锁音频视频播放新姿势——从界面设计到代码实践,全方位教你如何在WPF应用中集成流畅的多媒体功能
【8月更文挑战第31天】本文以随笔形式介绍了如何在WPF应用中集成音频和视频播放功能。通过使用MediaElement控件,开发者能轻松创建多媒体应用程序。文章详细展示了从创建WPF项目到设计UI及实现媒体控制逻辑的过程,并提供了完整的示例代码。此外,还介绍了如何添加进度条等额外功能以增强用户体验。希望本文能为WPF开发者提供实用的技术指导与灵感。
183 0
|
4月前
|
存储 安全 Java
Hashtable 和 HashMap 的区别
【8月更文挑战第22天】
175 0
|
2月前
|
Java
让星星⭐月亮告诉你,HashMap中保证红黑树根节点一定是对应链表头节点moveRootToFront()方法源码解读
当红黑树的根节点不是其对应链表的头节点时,通过调整指针的方式将其移动至链表头部。具体步骤包括:从链表中移除根节点,更新根节点及其前后节点的指针,确保根节点成为新的头节点,并保持链表结构的完整性。此过程在Java的`HashMap$TreeNode.moveRootToFront()`方法中实现,确保了高效的数据访问与管理。
32 2
|
2月前
|
Java 索引
让星星⭐月亮告诉你,HashMap之往红黑树添加元素-putTreeVal方法源码解读
本文详细解析了Java `HashMap` 中 `putTreeVal` 方法的源码,该方法用于在红黑树中添加元素。当数组索引位置已存在红黑树类型的元素时,会调用此方法。具体步骤包括:从根节点开始遍历红黑树,找到合适位置插入新元素,调整节点指针,保持红黑树平衡,并确保根节点是链表头节点。通过源码解析,帮助读者深入理解 `HashMap` 的内部实现机制。
42 2