ConcurrentHashMap静态公共方法分析
文章目录
分析ConcurrentHashMap中一些重要的公共方法,以便理解后面的源码。
1. spread 方法
spread()
方法的作用是为了将hash值变得更加散列,从而减少hash冲突的产生。
翻译过来的方法注释:
“将哈希值较高的位传播到较低的位,并强制顶位为0。因为表使用2的次方掩码,所以仅在当前掩码以上的位上变化的散列集总是会碰撞。(已知的例子是在小表中保存连续整数的Float键集。)所以我们应用一个变换,向下传播更高位的影响。比特传播的速度、效用和质量之间需要权衡。因为许多常见的散列集已经合理分布(因此不会从扩散中受益),而且因为我们使用树来处理箱子中的大型碰撞集,所以我们只是以尽可能便宜的方式对一些移位的位进行XOR,以减少系统损失,以及合并由于表边界而永远不会在索引计算中使用的最高位的影响。”
/**
* 计算Node节点hash值的算法:参数h为hash值
* 例如:
* h二进制为 --> 1100 0011 1010 0101 0001 1100 0001 1110
* (h >>> 16) --> 0000 0000 0000 0000 1100 0011 1010 0101
* (h ^ (h >>> 16)) --> 1100 0011 1010 0101 1101 1111 1011 1011
* 注:(h ^ (h >>> 16)) 目的是让h的高16位也参与寻址计算,使得到的hash值更分散,减少hash冲突产生
* ------------------------------------------------------------------------------
* HASH_BITS --> 0111 1111 1111 1111 1111 1111 1111 1111
* (h ^ (h >>> 16)) --> 1100 0011 1010 0101 1101 1111 1011 1011
* (h ^ (h >>> 16)) & HASH_BITS --> 0100 0011 1010 0101 1101 1111 1011 1011
* 注: (h ^ (h >>> 16))得到的结果再& HASH_BITS,目的是为了让得到的hash值结果始终是一个正数
*/
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}
2. tabAt 方法
tabAt()
方法用于获取 Node 数组 table(桶)指定下标位置上的 Node 节点。
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale)
,在静态代码块中ASHIFT
被赋值,ConcurrentHashMap采用了位运算的方式来计算数组中元素的偏移地址而不是用乘法,这样会更加高效。
/**
* 获取 Node 数组 table 中下标为 i 的 Node 节点
* tab Node数组引用
* i 数组下标
*/
@SuppressWarnings("unchecked")
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
// getObjectVolatile() 是native方法,作用是从主存中获取线程共享变量
// ((long)i << ASHIFT) + ABASE 通过运用位运算计算出目标节点的偏移地址
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
3. casTabAt 方法
/**
* cas方式将table中的元素更新
* tab Node数组table(哈希表)
* i 要替换的元素的下标
* c 期望节点值
* v 新的节点值
*/
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
// 调用Unsafe类的compareAndSwapObject()方法
// 与tabAt方法一样计算出元素的偏移地址
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
4. setTabAt 方法
/**
*
* tab Node数组table(哈希表)
* i table数组下标
* v 要设置的值
*/
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
// 调用Unsafe中的putObjectVolatile()方法,设置主存中变量的值
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
5. resizeStamp 方法
/**
* 返回用于调整大小为n的表大小的戳位。当被RESIZE_STAMP_SHIFT左移时必须为负。
*/
static final int resizeStamp(int n) {
return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
}
举例分析:
假设table
需要冲16扩容到32时,numberOfLeadingZeros(16)
的返回值就是27。
RESIZE_STAMP_BITS
的值是16,经过(1 << (RESIZE_STAMP_BITS - 1)
运算后再与27做与运算,就会得到一个扩容表示戳。
6. tableSizeFor 方法
tableSizeFor
与HashMap中的tableSizeFor
方法一样,传入一个数,通过位运算返回大于等于这个数的最小2次幂的数。
private static final int tableSizeFor(int c) {
int n = c - 1;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;
}