1.spread(int h)方法
ConcurrentHashMap中key的hash值的计算方式,是通过spread() 方法计算得到的。
static final int spread(int h) {
/*
* 其中h是key的hashCode()得到的。
* h ^ (h >>> 16)就是让高16位与低16位进行异或,使得计算出来的哈希值更加散列。
*
* 然后 & HASH_BITS(二进制是31个1)就是为了让最终得到的哈希值始终是一个正数。
*/
return (h ^ (h >>> 16)) & HASH_BITS;
}
2.tabAt方法
/*
* 方法作用: 就是通过UnSafe的本地方法直接操作内存获取table中指定位置的元素,直接操作内存,效率高。
*
* @return 获取哈希表中指定位置的Node
* @param tab 表示哈希表
* @param i 需要获取元素的索引
*/
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
/*
* U : UnSafe对象实例
* ABASE : 数组对象头大小
*
* (i << ASHIFT + ABASE) = (ABASE + Scale(单个Node的内存大小) * i) 就是
* 得到当前Node[]数组下标为i的节点对象的偏移地址
*
* 然后,通过getgetObjectVolatile()直接获取内存中指定位置的Node。
*/
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
3.casTabAt
/*
* 作用: 底层通过UnSafe的本地方法,通过CAS的方式向哈希表中的指定位置设置节点,直接操作内存,效率更高
*
* @param tab: 表示Node[]数组
* @param i 表示数组下标
* @param c 表示期望节点值
* @param v 表示要设置的节点值
*
*/
static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
Node<K,V> c, Node<K,V> v) {
/*
* native方法
* U: UnSafe对象的实例
* ((long)i << ASHIFT) + ABASE : 内存偏移地址
* c: 期望节点值
* v: 要设置的节点值
*/
return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}
4.setTabAt方法
/*
* 根据下标i,设置table中对应位置的节点。
*
* @param tab 表示table数组
* @param i 要设置节点索引位置
* @param 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);
}
通过上面的三个方法可以得到,ConcurrentHashMap无论是获取指定的Node还是设置Node都是直接通过UnSafe操作的内存,效率更高。
5.resizeStamp方法
//table数组扩容时,计算出一个扩容标识戳,当需要并发扩容时,当前线程必须拿到扩容标识戳才能参与扩容
static final int resizeStamp(int n) {
/*
* RESIZE_STAMP_BITS :固定值16,与扩容相关,计算扩容时会根据该属性生成一个扩容标识戳
*
* 当我们需要table容量从16扩容到32时,Integer.numberOfLeadingZeros(16)会得到
* 27,怎样得到的?
*
* numberOfLeadingZeros(int n)方法,会根据传入的n,返回当前数值转换为二进制后,
* 从高位开始统计,统计有多少个连续的0
* 16的二进制表示为10000,Integer占32位,10000有5位,所以连续最长的0有27位
*
* 1 << 15 = 32768
*
* 两者进行|操作得到扩容标识戳
*/
return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1)); }
6.tableSizeFor方法
/*
* 传入一个int类型的变量c,返回一个大于或等于c的 最小的2的幂。
* 比如传入 16 返回16
* 传入 17 返回 32
*/
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;
}