上一篇文章中说到如何使用IDEA搭建JDK1.8阅读学习环境,JDK1.8源码下载及获取、导入IDEA阅读、配置JDK源码。这篇文章将学习ConcurrentHashMap源码
🍀ConcurrentHashMap初始化
在jdk8的ConcurrentHashMap中一共有5个构造方法,这5个构造方法中都没有对内部的数组做初始化, 只是对一些变量的初始值做了处理。
jdk8的ConcurrentHashMap的数组初始化是在第一次添加元素时完成
// 没有维护任何变量的操作,如果调用该方法,数组长度默认是16
public ConcurrentHashMap() {
}
// 传递进来一个初始容量,ConcurrentHashMap会基于这个值计算一个比这个值大的2的幂次方数作为初始容量
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);
}
// 调用四个参数的构造
public ConcurrentHashMap(int initialCapacity, float loadFactor) {
this(initialCapacity, loadFactor, 1);
}
// 计算一个大于或者等于给定的容量值,该值是2的幂次方数作为初始容量
public ConcurrentHashMap(int initialCapacity,
float loadFactor, int concurrencyLevel) {
if (!(loadFactor > 0.0f) || initialCapacity < 0 || concurrencyLevel <= 0)
throw new IllegalArgumentException();
if (initialCapacity < concurrencyLevel) // Use at least as many bins
initialCapacity = concurrencyLevel; // as estimated threads
long size = (long)(1.0 + (long)initialCapacity / loadFactor);
int cap = (size >= (long)MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY : tableSizeFor((int)size);
this.sizeCtl = cap;
}
☘️JDK1.8中使用new ConcurrentHashMap(16)初始化,初始容量是多少?
JDK1.7 中ConcurrentHashMap(16),则初始容量是16。 JDK1.8则为32。
再回顾下ConcurrentHashMap(int initialCapacity)构造方法,
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;
}
主要看这个tableSizeFor(initialCapacity + (initialCapacity >>> 1) + 1))方法,参数为16 + 8 + 1。
// 返回设置值2倍的最近的2的次幂,前后距离相等时,向取取(3*2 = 6,距4和8一样,取8)
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;
}
最终得到的初始容量为32。
☘️sizeCtl解释理解
sizeCtl变量是ConcurrentHashMap中一个非常重要的变量,而且具有非常丰富的含义。
它的值不同,对应的含义也不一样,这里我们先对这个变量不同的值的含义做一下说明,后续源码分析过程中,进一步解释。
| sizeCtl初始值 | 含义 |
|---|---|
| 0 | 数组未初始化, 且数组的初始容量为16 |
| 正数 | 如果数组未初始化,那么其记录的是数组的初始容量;如果数组已经初始化,那么其记录的是数组的扩容阈值(数组的初始容量*0.75) |
| -1 | 数组正在进行初始化 |
| 小于0,并且不是-1 | 数组正在扩容, -(1+n),表示此时有n个线程正在共同完成数组的扩容操作 |