ConcurrentHashMap(jdk1.8)
底层操作
//数组对象 = 头对象(8bytes) + 指针对象(4bytes) + 数组长度(4bytes) + 数据(xxxbytes)
Class<?> ak = Node[].class; //Node[]Class 对象ak
ABASE = U.arrayBaseOffset(ak); //头对象 + 指针对象 + 数组对象 = 16
int scale = U.arrayIndexScale(ak); //单个元素的大小 int(4bytes) long(8bytes)
if ((scale & (scale - 1)) != 0) //scale必须是2的幂
throw new Error("data type scale not a power of two");
/**
Integer.numberOfLeadingZeros(n) 计算n的二进制从最高有效位往左有几个0
案例: n = 4 二进制 100 32-3=29
ASHIFT = 31-29 = 2
**/
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
/**
该方法的目的: 取出table[i]
(i << ASHIFT) + ABASE i是角标
接着上面的案例: i* 4 + 16
相当于数组的寻址公式 a[i]_address = data_type_size*i + base_adress
**/
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
/**
该方法的目的: CAS操作 更新值为v table[i] = v
((long)i << ASHIFT) + ABASE 这个上面说了就是寻址公式能够找到该元素 在底层所在的位置
**/
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
一、 容器初始化
1.源码分析
public ConcurrentHashMap() {
}
public ConcurrentHashMap(int initialCapacity) {
if (initialCapacity < 0)
throw new IllegalArgumentException();
int cap = ((initialCapacity >= (MAXIMUM_CAPACITY >>> 1)) ?
MAXIMUM_CAPACITY :
tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1));
this.sizeCtl = cap;
}
//基于一个Map集合,构建一个ConcurrentHashMap
//初始容量为16
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
this.sizeCtl = DEFAULT_CAPACITY;
putAll(m);
}
2.sizeCtl含义解释
注意: 以上这些构造方法中都涉及到一个变量sizeCtl,这个变量是一个非常重要的变量。
而且具有非常丰富的含义,它的值不同,对应的含义也不一样,这里先做一些简单说明:
sizeCtl
为0, 代表数组未初始化,且数组的初始容量为16
sizeCtl
为正数,如果数组未初始化,那么其记录的是数组的初始容量,如果数组已经初始化,那么其记录的是数组的扩容阈值 (数组的初始容量*0.75 sc = n - (n >>> 2)