首先介绍一下ConcurrentHashMap的成员变量和常量
Constants
private static final int MAXIMUM_CAPACITY = 1 << 30; //最大容量2的30次方
private static final int DEFAULT_CAPACITY = 16; //默认容量 1<<4
private static final float LOAD_FACTOR = 0.75f; //负载因子
static final int TREEIFY_THRESHOLD = 8; //链表转为红黑树,大于8小于6先对链表数组进行翻倍扩容操作
static final int UNTREEIFY_THRESHOLD = 6; //树转列表
static final int MIN_TREEIFY_CAPACITY = 64; //链表真正转为红黑树
private static final int MIN_TRANSFER_STRIDE = 16;
private static int RESIZE_STAMP_BITS = 16;//stamp高位标识移动位数
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
static final int MOVED = -1; // forwarding nodes 的hash值,如果hash值等于-1代表线程协助扩容
static final int TREEBIN = -2; // roots of trees 的hash值,如果hash等于-2代表,当前桶是红黑树
static final int RESERVED = -3; // transient reservations 的hash值
// usable bits of normal node hash,在hash计算的时候运用到,与HashMap计算出来的hash值进行与操作
static final int HASH_BITS = 0x7fffffff;
static final int NCPU = Runtime.getRuntime().availableProcessors(); //可用处理器数量
Fields
transient volatile Node<K,V>[] table;//当前ConcurrentHashmap的Node数组
private transient volatile Node<K,V>[] nextTable;//ForwardNode所指向的下一个表
private transient volatile long baseCount;//如果使用CAS计数成功,使用该值进行累加
//扩容设置的参数,默认为0,当值=-1的时候,代表当前有线程正在进行扩容操作
//当值等于-n的时候,代表有n个线程一起扩容,其中n-1线程是协助扩容
//当在初始化的时候指定了大小,这会将这个大小保存在sizeCtl中,大小为数组的0.75
private transient volatile int sizeCtl;
private transient volatile int transferIndex;
private transient volatile int cellsBusy;
//如果使用CAS计算失败,也就是说当前处于高并发的情况下,那么
//就会使用CounterCell[]数组进行计数,类似jdk1.7分段锁的形式,锁住一个segment
//最后size()方法统计出来的大小是baseCount和counterCells数组的总和
private transient volatile CounterCell[] counterCells;
然后我们介绍一下ConcurrentHashMap为我们提供的三个核心的原子方法:
//获取当前地址偏移量的Node对象
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);
}
//对比当前地址偏移量的对象和第三个参数的对象是否为同一个对象
//一样则将这个对象替换成第四个参数的对象并且返回true
//不一样则直接返回一个false
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);
}
//将当前地址偏移量的对象设置成第三个参数的对象
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
三个方法的作用都在代码上注释了,三个方法中调用的方法都是native类型的无法直接看到源码,那么我们分析一下,他是怎么获取到地址偏移量的,在代码中我们看到了他进行了这样的转换
((long)i << ASHIFT) + ABASE
这条代码中有两个变量,我解释一些这两个变量
ABASE:是针对当前数组首个元素的地址偏移量
ASHIFT:是针对当前数组某个位置上元素所占字节并且经过相对应的处理而产生的结果,下面会重点讲解一下这个
首先是ABASE变量的获取:
ABASE = U.arrayBaseOffset(对象);
U是Unsafe的实例对象,这个对象不能轻易获取到,不过可以通过反射来获取,示例:
try {
Field f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
U = (Unsafe) f.get(null);
int scare = U.arrayIndexScale(String[].class);
System.out.println(scare);
} catch (Exception e) {
e.printStackTrace();
}
在ConcurrentHashMap中,传入这个方法的对象其实不是对象本身,而是对象的一个引用,所以我们来看下ASHIFT变量获取的方法
首先通过
int scale = U.arrayIndexScale(ak);
来进行获取当前数组元素所占字节,因为是对象引用,所以都是返回4
然后再进行第二步的处理:
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
这个地方才是真的为ASHIFT进行赋值的地方,后一个方法返回的是参数的二进制代码左侧包含0的个数,在当前实例中,参数为4,二进制表示为100,前面含有29个零,所以这个变量被赋值为2
这样我们就可以总结一下,地址偏移量 = (传入值 << ASHIFT) + ABASE
这三个方法频繁的在CAS算法中使用,所以要先了解一下。
下面分析一下ConcurrentHashMap的构造方法,这个类有五个构造方法分别是
1、public ConcurrentHashMap()//无参构造方法
2、public ConcurrentHashMap(int initialCapacity)//参数为容器容量
3、public ConcurrentHashMap(Map<? extends K, ? extends V> m)//参数为传入的map集合
4、public ConcurrentHashMap(int initialCapacity, float loadFactor)//参数1容量,参数2负载因子
5、public ConcurrentHashMap(int initialCapacity,float loadFactor, int concurrencyLevel)
//第一个参数容量,第二个参数负载因子,第三个参数并发级别,用来确定segment的
这里我主要拿一个构造方法出来分析:
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;
}
这个构造方法,我们可以看出,如果你传入的参数是容量大小,那么他在进行向上找2倍数最近值的时候,他传入tableSizeFor()方法的参数是之前传入容量的1.5倍+1,这个地方在后头链表超过长度8开始转换的时候也有同样的操作,也就是说,扩容是按照两倍来扩容的,最后将获得的值cap赋值给sizeCTL,这边也进一步说明了,sizeCTL为正代表的是容量的大小。
参考链接:
https://blog.csdn.net/u012129558/article/details/52268194
https://blog.csdn.net/u010723709/article/details/48007881
https://www.jianshu.com/p/9819eb48716a
https://www.jianshu.com/p/c17ca559886d
网友评论