ConcurrentHashMap实现原理

ConcurrentHashMap实现原理

1.HashMap实现原理
 HashMap是线程不安全的。HashMap是由数组+链表实现的,当无冲突发生时是在数组中存放,当发生hash冲突时在该位置下面挂链表,链表越长代表冲突越多。在多线程情况下,在put的时候,插入的元素超过了容量就会触发扩容操作,即rehash,此时将该hash表中的元素重新hash到另一个更大容量的hash表,在多线程环境下,存在同时其他元素也在进行put操作,如果hash值相同,可能在同一链表下出现闭环,从而导致出现死循环。

2.HashTable实现原理
 HashTable是线程安全的。它在所有涉及多线程操作的代码块上都加了synchronized关键字来锁住整个table,但线程安全的代价是效率很低。

3.ConcurrentHashMap(JDK1.7的实现)
 利用锁分离的技术。由一个Segment数组和多个HashEntry组成。Segment数组的意义就是将一个大的table分成很多小table来进行加锁,每一个Segment元素存储的是HashEntry数组+链表。
(1)初始化
 它的初始化是通过位与运算来初始化Segment的大小,用ssize来表示。
(2)put操作
 ConcurrentHashMap的插入需要进行两次定位,第一次是找Segment,第二次找HashEntry。Segment继承了ReentrantLock,它每次找HashEntry使用重入锁的tryLock(),如果获取锁成功则直接插入相应位置,若是失败会以自旋方式继续尝试,直到超过指定次数,被挂起。
(3)get操作
 还是两次。
(4)size操作(因为是并发的,所以很难直接确定大小,有两种方案)
 1.使用不加锁的模式去尝试多次计算ConcurrentHashMap的size,最多三次,比较前后两次计算的结果,结果一致就认为没有元素插入,当前计算结果为正确。
 2.如果第一种方案得到的计算结果不一致,就给每个Segment加上锁,然后计算size。

4.ConcurrentHashMap(JDK1.8的实现)
 摒弃了Segment的概念,直接用Node数组+链表+红黑树实现,并发控制使用synchronized和CAS操作。

(1)数据结构
 Node:ConcurrentHashMap存储结构的基本单元,继承于HashMap中的Entry,用于存储数据。就是一个链表,只允许查找,不允许修改。
 TreeNode:继承于Node,数据结构换成了二叉树,用于红黑树中存储数据,当链表的结点数大于8时会转换成红黑树结构。
 TreeBin:封装TreeNode的容器,提供转换红黑树的一些条件和锁的控制。
(2)put操作
 1.如果没有初始化就先调用initTable()方法来进行初始化过程
 2.如果没有hash冲突就直接CAS插入。
 3.如果还在进行扩容操作就先进行扩容。
 4.如果存在hash冲突,就加锁来保证线程安全,这里有两种情况,一种是链表形式就直接遍历到尾端插入,一种是红黑树就按照红黑树格式插入。
 5.如果该链表的数量大于阈值8,就要先转换成红黑树结构,break后再一次进入循环。
 6.如果添加成功就调用addCount()方法统计size,并且检查是否需要扩容。
(3)get操作
 1.计算hash值,定位到该table索引位置,如果是首结点符号就返回。
 2.如果遇到扩容的时候,会调用标志正在扩容节点ForwardingNode的find方法,查找该节点,匹配就返回。
 3.以上都不符合的话,就往下遍历节点,匹配就返回,否则最后就返回null。

你可能感兴趣的:(Java高并发,java,高并发,hashmap)