容器——ConcurrentHashMap的底层实现原理

ConcurrentHashMap的底层实现原理

1、HashMap线程不安全

HashMap是线程不安全的。
HashMap在并发执行put操作是会引起死循环,是因为多线程情况下在put()时进行resize()的扩容操作,执行transfer()的时候会导致HashMap的Entry链表形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环。

Map接口(代码模拟HashMap的底层实现)

HashMap的底层实现原理?(JDK1.7与JDK1.8源码分析对比)

2、HashTable效率低下

HashTable使用synchronized来保证线程的安全,但是在线程竞争激烈的情况下锁住整张表,效率非常低下。
当一个线程访问HashTable的同步方法,其他方法访问HashTable的同步方法时,会进入阻塞或者轮询状态。如果线程1使用put进行元素添加,线程2不但不能用put方法添加于元素同是也无法用get方法来获取元素,所以竞争越激烈效率越低。
HashMap和HashTable的区别?

3、ConcurrentHashMap的底层实现原理

3.1JDK1.7中的ConcurrentHashMap

JDK1.7中,ConcurrentHashMap采用的是锁分段技术。

HashTable容器在竞争激烈的并发环境下表现出效率低下的原因是所有访问HashTable的线程都必须竞争同一把锁,假如容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效提高并发访问效率,这就是ConcurrentHashMap所使用的锁分段技术。首先将数据分成一段一段地存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

jdk1.7中采用Segment + HashEntry的方式进行实现,结构如下:
容器——ConcurrentHashMap的底层实现原理
ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁(ReentrantLock),在ConcurrentHashMap里扮演锁的角色;HashEntry则用于存储键值对数据。一个ConcurrentHashMap里包含一个Segment数组。Segment的结构和HashMap类似,是一种数组和链表结构。一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,必须首先获得与它对应的Segment锁,如下图所示。

3.2JDK1.8中的ConcurrentHashMap

1.7中解决了并发问题,并且能支持 N 个 Segment 这么多次数的并发,但依然存在 HashMap 在 1.7 版本中的问题。那就是查询遍历链表效率太低
1.8中放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现。锁住的是一个Node。
总体结构是数组+链表(红黑树),实现线程安全是通过Node+CAS+Synchronized实现的。
容器——ConcurrentHashMap的底层实现原理