JAVA集合类

【解疑】ConcurrentHashMap 在JDK1.7时候put或get时候,怎么定位到数据的?

在面试的时候,ConcureentHashMap在JDK1.7的时候线程安全底层具体实现方式是什么?CouncureentHashMap在JDK1.7的时候如下图:ConcurrentHashMap由Segment数组组成,Segment继承了ReentrantLock可以提供锁的功能,也表示并发度,是最大并行访问的线程数量,每一个Segment内部包含一个HashEntry数组用于元素存储,Ha

在面试的时候,ConcureentHashMap在JDK1.7的时候线程安全底层具体实现方式是什么?

88794461cd00e5d195b2439697aed180.png

CouncureentHashMap在JDK1.7的时候如下图:

9bcf37b474e4740d506a5f401da0f4c3.png

ConcurrentHashMap由Segment数组组成,Segment继承了ReentrantLock可以提供锁的功能,也表示并发度,是最大并行访问的线程数量,每一个Segment内部包含一个HashEntry数组用于元素存储,


HashEntry则是一个K-V的存储单元,尾部可以挂HashEntry使用链地址法解决hash冲突


1.7中的ConcurrentHashMap源码大约1600行,去除大量注释外,我们只需要关注核心方法,


segment大小固定后,就不可变了默认是16个。也就是说,默认支持16个线程并发写。

16个segment就是16把锁(门牌号),那么在put的时候,是怎么定位到那获取哪个门牌号?数据是怎么put进去的?

ConcurrentHashMap定位一个元素需要两次Hahs,,操作,第一次Hash定位到Segement,第二次Hash定位到元素所在的链表的头部.这种结构下,Hash过程比普通的HashMap要久,但是写操作的时候,只对元素所在的Segement加锁即可,不会影响其他的Segement.在理想情况下,ConcurrentHashMap最高可以同时支持Segement数量大小的写操作.正因为这样,ConcurrentHashMap的并发能力得以提高.

我们来看看源码:

  • put操作先定位Segment,再定位HashEntry,需要进行2次Hash操作,下面是先定位到Segment

public V put(K key, V value) {
        Segment<K,V> s;
        //1.value不能为null
        if (value == null) throw new NullPointerException();
        int hash = hash(key);
        int j = (hash >>> segmentShift) & segmentMask;
        //2.定位Segemnt,如果为null就先初始化,CAS操作
        if ((s = (Segment<K,V>)UNSAFE.getObject          // nonvolatile; recheck
             (segments, (j << SSHIFT) + SBASE)) == null) //  in ensureSegment
            s = ensureSegment(j);
        //使用Segment的put操作
        return s.put(key, hash, value, false);
}

502af3abbb9414c4afe7cf781cd94b55.png

在得到对应的segment之后,调用s.put方法

Segment 的结构和 HashMap 类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个 HashEntry 数组里的元素,当对 HashEntry 数组的数据进

原创不易,完成人机校验,阅读全文

相关推荐