JDK1.7
1、 首先将数据分为一段一段的存储, 然后给每一段数据配一把锁, 当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
2、 在JDK1.7中,ConcurrentHashMap采用Segment +HashEntry的方式进行实现,结构如下∶
3、 一个ConcurrentHashMap 里包含一个Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个HashEntry 数组,每个 HashEntry 是一个链表结构的元素。
1、 该类包含两个静态内部类 HashEntry 和 Segment ;前者用来封装映射表的键值对,后者用来充当锁的角色;
2、 Segment 是一种可重入的锁 ReentrantLock,每个 Segment 守护一个HashEntry 数组里得元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment 锁。
JDK1.8
在JDK1.8中,放弃了Segment臃肿的设计,取而代之的是采用Node+CAS+synchronized来保证并发安全进行实现,synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
结构如下∶
1、附加源码,有需要的可以看看
2、插入元素过程(建议去看看源码)
3、如果相应位置的Node还没有初始化,则调用CAS插入相应的数据
else if((f = tabAt(tab,i=(n-1)& hash))== null){
if (casTabAt(tab, i, null, new Node < K, V > (hash, key, value, null)))
break;//no lock when adding to empty bin
}
如果相应位置的Node不为空,且当前该节点不处于移动状态,则对该节点加synchronized锁,如果该节点的hash不小于0,则遍历链表更新节点或插入新节点
if(fh>=0){
binCount = 1;
for(Node < K,V> e= f;;++binCount){
K ek;
if (e.hash == hash &&((ek = e.key)== key||(ek!= null &&key.equals(ek)))){
oldVal=e.val;
if (!onlyIfAbsent) e.val = value;
break;
}
Node < K, V> pred = e;
if((e = e.next)== null){
pred.next = new Node < K, V> (hash, key, value, null);
break;
}
}
}
1、如果该节点是TreeBin类型的节点,说明是红黑树结构,则通过putTreeVal 方法往红黑树中插入节点;如果binCount不为0,说明put操作对数据产生了影响,如果当前链表的个数达到8个,则通过 treeifyBin 方法转化为红黑树,如果oldVal不为空,说明是一次更新操作,没有对元素个数产生影响,则直接返回旧值
2、如果插入的是一个新节点,则执行addCount()方法尝试更新元素个数baseCount
Was this helpful?
0 / 0