- 😜作 者:是江迪呀
- ✒️本文关键词:
Java
、集合
、Map
、CurrentHashMap
- ☀️每日 一言:
坚持自己的风格,面对未知的一切!
HashMap
、HashTable
、CurrentHashMap
对比CurrentHashMap
和HashMap
对比由于HashMap
在多线程下不安全诞生了HashTable
由于HashTable
效率太低诞生了CurrentHashMap
。CurrentHashMap
它大量的使用了volatile
,final
,CAS
等lock-free
技术来减少锁竞争对于性能的影响。ConcurrentHashMap
避免了对全局加锁改成了局部加锁操作。这样就极大的提高了 并发环境下的操作速度。
所以CurrentHashMap
线程是安全的,HashMap
线程不安全,在多线程下,使用HashMap
进行put
操作或造成死循环,导致CPU
的利用率接近100%,使用并发情况下不能使用HashMap
HashMap
和HashTable
对比HashMap
和HashTable
的实现原理几乎一致,差别是:HashTable
不允许key
和value
为nul
l。HashTable
是线程安全的。但是Hastable
实现线程安全的代价很大,get/put
的所有相关操作都是synchronized
的,这相当于给hash
表加上了一把大锁,多发生多线程操作HashTable
时,只要有一个线程操作HashTable
其它线程必须阻塞,相当于将所有多线程操作串行化,性能和效率很差。
ConcurrentHashMap
实现原理在JDK1.7中ConcurrentHashMap
使用的是数组+Segment
+分段锁
对的方式实现。Segment(分段锁)
–减少锁的粒度,ConcurrentHashMap
中的分段锁被称为Segment
,它类似与 HashMap
对的结构,及内部拥有一个Entry
数组,数组中的每个元素又是一个链表。同时又是一个ReentrantLock
(Segment继承了ReentrantLock
)
ConcurrentHashMap
的内部结构ConcurrentHashMap
采用分段锁技术,将数据分为一段一段存储, 然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其它段的数据也能被其它线程访问。能够实现真正意义的并发访问,ConcurrentHashMap
结构图:
从上面的结构我们可以看出,ConcurrentHashMap
定位一个元素的过程需要进行两次操作。第一次定位到底Segment
,第二次定位定位到元素所在的链表的头部。
好处:写操作的时候可以只对元素所在的Segment
进行加锁即可,不会影响到 其它的Segment
,这样在理想状态下,ConcurrentHashMap
可以最高同时支持和Segment
数量一样多的写操作(刚好这些写操作都非常平均的分配到,所有的Segment
上)并发能力可以大大的提高。
坏处:这种结构带来的副作用就是,存值时的过程要比HashMap
要长。Segment
的数量是根据并发总量来决定的,比如来说,并发总量是8,那么segment的数量就是8
ConcurrentHashMap
的并发能力可以大大的提高?JDK8中ConcurrentHashMap
采用了数组+链表+红黑树的实现方式来设计,内部采用大量的CAS
操作。
CAS
是compare and swap
的缩写,就是比较并交换。CAS
是一种基于锁的操作,是乐观锁。
锁分为乐观锁和悲观锁,悲观锁就是将资源锁住后,等当前获得锁的对象将锁释放后,下一个线程才能访问。而乐观锁采用了宽泛的态度,通过某种方式不加锁来处理资源,比如通过给记录加version来获取数据,性能较悲观锁有很大的提高。
CAS操作包含三个操作数:
(1)内存位置(V);
(2)预期值(A);
(3)新值(B);
如果内存地址里面的值和A
的值是一样的,那么就将内存中的值更新成B
,CAS
是通过无限循环来获取数据的,如果第一轮循环中,a
线程获取地址里面的值被b
线程修改了,那么a
线程需要自旋,到下次循环才有可能被执行。
JDK8中彻底放弃了Segment转而采用的是Node。其设计思想不再是1.7中的分段锁机制。
Node:保存key,value以及key的Hash值的数据结构,其中value和next都是用volatile修饰,保证并发内存的可见性。java8中的ConcurrentHashMap的结构基本和Java8中的HashMap一致,不过保证线程安全。在JDK8中的ConcurrentHashMap引入了红黑树,红黑树是一种性能非常好的二叉查找树,其查找性能是O。当ConcurrentHashMap中的链表的长度大于某个阈值(这个阈值是8)的时候将链表转化为红黑树,以提高查询效率。
ConcurrentHashMap
结构和Hashmap
的结构比较JDK1.8的ConcurrentHashMap
结构和Hashmap
的结构很接近,只是增加了同步操作来控制并发:
Segment
分段锁机制,采用了数组+链表+红黑树Segment
分段锁机制,其中Segment
继承ReentrantLock
。1.8采用CAS
+Synchronized
保证线程安全。Segment
加锁,1.8调整为对每个数组元素进行加锁(node)减小了锁的粒度,提高的性能。O(n)
,变成遍历红黑树O(LogN)