ConcurrentHashMap是线程安全且高效的HashMap。HaspMap可能在并发执行put的致使死循环发生,resize致使造成环。 HashTable线程安全,可是效率低下。算法
ConsurrentHashMap经过分段锁能够提高效率。假如容器里有多把锁,每一把锁用于锁容器中的一部分数据。将数据分红一段一段地存储,而后给每一段数据配一把锁。当一个线程占用锁访问其中一个段数据的时候,其余段的数据也能被其余线程访问。数组
ConconrrentHashMap是有segment数组结构和HashEntry数组结构组成。Segment是一种可重入锁(ReentrantLock),在ConcurrentHashMap里扮演锁的角色,HashEntry则用于存储键值对数据。Segement里包含一个HashEntry数组,每一个HashEntry是一个链表结构的元素,每一个Segment守护着一个HashEntry数组里的元素,当对HashEntry数组的数据进行修改时,首先要得到与他对应的Segment锁。安全
segments数组的长度ssize,为了能经过按位与的散列算法来定位segments数组的索引,必须保证segments数组的长度是2的N次方。并发
定位segment。既然ConcurrentHashMap使用分段锁Segement来保护,那么插入和获取元素的时候,必须先经过散列算法定位到segment。会首先使用Wang/Jenkins hash的变种算法对元素的hashCode进行一次再散列。线程
之因此进行再散列,目的是减小散列冲突,使元素可以均匀的分布在不一样的segement上。code
ConcurrentHashMap的操做索引
1.get操做rem
Segment的get操做实现很是简单和高效。先通过一次再散列,而后使用这个散列值经过散列运算定位到Segement,在经过散列算法定位到元素。get
定位HashEntry和定位Segment的散列算法虽然同样,都与数组的长度减去1在相“与”,可是相于的值不同,定位Segment使用的是元素的hashcode经过再散列后获得的值的高位,而定位HashEntry直接使用的再散列后的值。目的是避免两次散列后的值同样,虽然元素在Segment里散列了,可是确没有在HashEntry里散列开。hash
hash >>> segmentShift) & segementMask //定位Segment所使用的hash算法
int index=hash & (tab.len-1) //定位HashEntry所使用的hash算法
2.put操做
因为put方法里须要对共享变量进行写操做,必须加锁。put先定义到Segment里进行插入操做。插入操做须要经历两个步骤,第一步判断是否须要对Segment里的Entry数组进行扩容,第二步定位添加元素的位置,而后将其放在HashEntry数组里。
3.size操做
若是要统计整个ConcurrentHashMap里元素的大小,就必须统计全部Segment里元素的大小求和。Segment里的全局变量count是一个volatile变量。虽然相加时能够获取每一个Segment的count的最新值,可是可能累计先后的count发生了变化,那么统计结果就不许了。
由于在累加count操做中,以前累加过的count发送变化的概率很是小,ConcurrentHashMapd的作法是先尝试2次经过不锁住Segement的方式来统计各个Segment大小,若是统计的过程当中发生了变化,则在采用加锁的方式来统计全部Segment的大小。
可经过modCount变量,在put,remove和clean方法里操做元素将会将变量modCount进行加1,那么在统计size先后比较modCount是否发生变化,从而得知容器的大小是否发生变化。