网站首页 > 文章精选 正文
currentHashMap的介绍
currentHashMap是线程安全并且高效的一种容器,我们就需要研究一下currentHashMap为什么既能够保证线程安全,又可以保证高效的操作。
为什么使用currentHashMap,我们就需要和HashMap以及HashTable进行比较?
HashMap是线程不安全的,在多线程的情况下,HashMap的操作会引起死循环,导致CPU的占有量达到100%,所以在并发的情况下,我们不会使用HashMap。
死锁原因
在 HashMap 扩容的时候会调用 resize() 方法,就是这里的并发操作容易在一个桶上形成环形链表;这样当获取一个不存在的 key 时,计算出的 index 正好是环形链表的下标就会出现死循环。
HashTable其中使用synchronize来保证线程安全,即当有一个线程拥有锁的时候,其他的线程都会进入阻塞或者轮询状态,这样会使得效率越来越低。
而currentHashMap的锁分段技术可以有效的提高并发访问率HashTable访问效率低下的原因,就是因为所有的线程在竞争同一把锁。
如果容器中有多把锁,不同的锁锁定不同的位置,这样线程间就不会存在锁的竞争,这样就可以有效的提高并发访问效率,这就是currentHashMap所使用的锁分段技术将数据一段一段的存储,为每一段都配一把锁,当一个线程只是占用其中的一个数据段时,其他段的数据也能被其他线程访问。
jdk7 的currentHashMap
在JDK1.7中ConcurrentHashMap采用了数组+Segment+分段锁的方式实现。
采用Segment(分段锁)来减少锁的粒度,ConcurrentHashMap中的分段锁称为Segment,它即类似于HashMap的结构,即内部拥有一个Entry数组,数组中的每个元素又是一个链表,同时又是一个ReentrantLock(Segment继承了ReentrantLock)。
currentHashMap内部结构
ConcurrentHashMap使用分段锁技术,将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问,能够实现真正的并发访问。如下图是ConcurrentHashMap的内部结构图:
Segment默认是16,按理说最多同时支持16个线程并发读写,但是是操作不同的Segment,初始化时也可以指定Segment数量,每一个Segment都会有一把锁,保证线程安全。
该结构的优劣势
- 坏处是这一种结构的带来的副作用是Hash的过程要比普通的HashMap要长。
- 好处是写操作的时候可以只对元素所在的Segment进行加锁即可,不会影响到其他的Segment,这样,在最理想的情况下,ConcurrentHashMap可以最高同时支持Segment数量大小的写操作(刚好这些写操作都非常平均地分布在所有的Segment上)。
所以,通过这一种结构,ConcurrentHashMap的并发能力可以大大的提高。
JDK1.8的currentHashMap
JDK1.8的currentHashMap参考了1.8HashMap的实现方式,采用了数组+链表+红黑树的实现方式,其中大量的使用CAS操作.CAS(compare and swap)的缩写,也就是我们说的比较交换。
CAS是一种基于锁的操作,而且是乐观锁。java的锁中分为乐观锁和悲观锁。悲观锁是指将资源锁住,等待当前占用锁的线程释放掉锁,另一个线程才能够获取线程.乐观锁是通过某种方式不加锁,比如说添加version字段来获取数据。
CAS操作包含三个操作数(内存位置,预期的原值,和新值)。如果内存的值和预期的原值是一致的,那么就转化为新值。CAS是通过不断的循环来获取新值的,如果线程中的值被另一个线程修改了,那么A线程就需要自旋,到下次循环才有可能执行。
JDK8中彻底放弃了Segment转而采用的是Node,其设计思想也不再是JDK1.7中的分段锁思想。
Node:保存key,value及key的hash值的数据结构。其中value和next都用volatile修饰,保证并发的可见性。
Java8的ConcurrentHashMap结构基本上和Java8的HashMap一样,不过保证线程安全性。在JDK8中ConcurrentHashMap的结构,由于引入了红黑树,使得ConcurrentHashMap的实现非常复杂。
我们都知道,红黑树是一种性能非常好的二叉查找树,其查找性能为O(logN),早期完全采用链表结构时Map的查找时间复杂度为O(N),JDK8中ConcurrentHashMap在链表的长度大于某个阈值的时候会将链表转换成红黑树进一步提高其查找性能。
jdk8中currentHashMap原理图
总结
看完了整个 HashMap 和 ConcurrentHashMap 在 1.7 和 1.8 中不同的实现方式相信大家对他们的理解应该会更加到位。其实这块也是面试的重点内容,通常的套路是:
- 谈谈你理解的 HashMap,讲讲其中的 get put 过程。
- 1.8 做了什么优化?
- 是线程安全的嘛?
- 不安全会导致哪些问题?
- 如何解决?有没有线程安全的并发容器?
- ConcurrentHashMap 是如何实现的?1.7、1.8 实现有何不同?为什么这么做?
这一串问题相信大家仔细看完都能怼回面试官。
推荐阅读:
阿里P7级面试必问:分布式+高并发+Redis,不看我真的怕你后悔
49天含泪苦学这些分布式技术文档,一不小心,吊打了字节跳动面试官
原文链接:
https://mp.weixin.qq.com/s/X2PztiAqitz3j_9jMTmnzA
猜你喜欢
- 2025-03-18 系统性能优化与Java代码编写性能考虑
- 2025-03-18 面试必问之:Java 中 == 和 equals 的区别你知道吗
- 2025-03-18 为什么重写 equals时必须重写 hashCode 方法?
- 2025-03-18 一网打尽-HashMap面试题(面试hashmap底层实现原理)
- 2025-03-18 HashMap面试知识点合集,这一篇就够了
- 2025-03-18 Java并发系列 | ConcurrentHashMap源码分析
- 2025-03-18 ConcurrentHashMap的实现原理(JDK1.7和JDK1.8)
- 2025-03-18 HashMap底层实现原理以及线程安全实现
- 2025-03-18 不怕面试再问HashMap,一次彻底地梳理(原理+手写实现)
- 2025-03-18 java面试题——HashMap相关面试题
- 最近发表
- 标签列表
-
- newcoder (56)
- 字符串的长度是指 (45)
- drawcontours()参数说明 (60)
- unsignedshortint (59)
- postman并发请求 (47)
- python列表删除 (50)
- 左程云什么水平 (56)
- 计算机网络的拓扑结构是指() (45)
- 稳压管的稳压区是工作在什么区 (45)
- 编程题 (64)
- postgresql默认端口 (66)
- 数据库的概念模型独立于 (48)
- 产生系统死锁的原因可能是由于 (51)
- 数据库中只存放视图的 (62)
- 在vi中退出不保存的命令是 (53)
- 哪个命令可以将普通用户转换成超级用户 (49)
- noscript标签的作用 (48)
- 联合利华网申 (49)
- swagger和postman (46)
- 结构化程序设计主要强调 (53)
- 172.1 (57)
- apipostwebsocket (47)
- 唯品会后台 (61)
- 简历助手 (56)
- offshow (61)