0
点赞
收藏
分享

微信扫一扫

ConcurrentHashMap源码解析

如果想更详细了解ConcurrentHashMap 内部工作原来可以去看下我以前写过一篇​​HashMap源码解析​​了解下hash 算法原理、数组扩容、红黑树转换等等。

initTable

private final Node<K,V>[] initTable() {
Node<K,V>[] tab; int sc;
while ((tab = table) == null || tab.length == 0) {
if ((sc = sizeCtl) < 0) //这时已经有其他线程获取到执行权,沉睡一会
Thread.yield(); // lost initialization race; just spin
else if (U.compareAndSetInt(this, SIZECTL, sc, -1)) {
try {
if ((tab = table) == null || tab.length == 0) {
int n = (sc > 0) ? sc : DEFAULT_CAPACITY;
@SuppressWarnings("unchecked")
Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; //初始化数组
table = tab = nt;
sc = n - (n >>> 2);
}
} finally {
sizeCtl = sc;
}
break;
}
}
return tab;
}

这个是初始化map数组,核心是sizeCtl 这个变量:一个使用volatile修饰共享变量,作用通过交换比较竞争获取初始化或者扩容数组执行权。当线程发现sizeCtl小于0的时候,线程就会让出执行权,当线程成功竞争设置-1 就相当于获取到执行权,所以就可以去初始化数组了。当为正数时,保存下一个要调整Map大小的元素计数值

说明下Unsafe 交换比较方法

/**
* 通过对象属性的值,修改前、更新后一致,才是更新成功
* @param o 需要被修改的属性的对象
* @param l 对象Field的指针地址,可以理解成属性值引用
* @param i 修改前的值
* @param i1 修改后的值
* @return
*/
public final native boolean compareAndSwapInt(java.lang.Object o, long l, int i, int i1)

put方法解析

public V put(K key, V value) {
return putVal(key, value, false);
}


final V putVal(K key, V value, boolean onlyIfAbsent) {
//ConcurrentHashMap 不允许key value为空,因为在并发情况下不能通过获取get(key) 判断key存不存在
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh; K fk; V fv;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) { //通过计算hash得到数组下标,为空通过交换比较设置进去,这时不需要加锁的
if (casTabAt(tab, i, null, new Node<K,V>(hash, key, value)))
break; // no lock when adding to empty bin
}
else if ((fh = f.hash) == MOVED) //这种情况说明数组正在扩容,需要对链表和黑红树进行迁移
tab = helpTransfer(tab, f);
else if (onlyIfAbsent // check first node without acquiring lock
&& fh == hash
&& ((fk = f.key) == key || (fk != null && key.equals(fk)))
&& (fv = f.val) != null)
return fv;
举报

相关推荐

0 条评论