ConCurrentHashMap的早期实现如上图,是将其分为很多segment,其次每个segment的数据结构类似于HashMap;每个Segment里面都拥有一个HashEntry<k,v>对象。 HashEntry<k,v> 内部使用了 volatile 的value字段保证了数据的可见性以达到同步的效果。在同步环境下,整体采用了分段锁定的效果,当其中一个segment进行同步操作时候,其他无关的segment也可以进行操作。这一点与HashTable 简单粗俗的 Sychronized 所有 add,put等方法 明显效率得多!
1.7JDK
put加锁
通过分段加锁segment,一个hashmap里有若干个segment,每个segment里有若干个桶,桶里存放K-V形式的链表,put数据时通过key哈希得到该元素要添加到的segment,然后对segment进行加锁,然后在哈希,计算得到给元素要添加到的桶,然后遍历桶中的链表,替换或新增节点到桶中
size
分段计算两次,两次结果相同则返回,否则对所以段加锁重新计算
1.8JDK
put CAS 加锁
1.8中不依赖与segment加锁,segment数量与桶数量一致;
首先判断容器是否为空,为空则进行初始化利用volatile的sizeCtl作为互斥手段,如果发现竞争性的初始化,就暂停在那里,等待条件恢复,否则利用CAS设置排他标志(U.compareAndSwapInt(this, SIZECTL, sc, -1));否则重试
对key hash计算得到该key存放的桶位置,判断该桶是否为空,为空则利用CAS设置新节点
否则使用synchronize加锁,遍历桶中数据,替换或新增加点到桶中
最后判断是否需要转为红黑树,转换之前判断是否需要扩容
size
利用LongAdd累加计算