目录
桶下面维护的有普通链表,也有树节点(红黑树),树节点下面有树结构和双向链表
1、ConcurrentMap中的变量
部分变量:
* 散列表数组最大值(是容量的最大值, 和size比较的)
private static final int MAXIMUM_CAPACITY = 1 << 30;
* 散列表默认值
private static final int DEFAULT_CAPACITY = 16;
* 并发级别,jdk1.7遗留下来的,1.8只有在初始化的时候用一用
* 不代表并发级别,
* 1.8中的并发级别是散列表的长度决定的
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;
* 负载因子,是固定值,不可修改
private static final float LOAD_FACTOR = 0.75f;
* 树化阈值,指定桶位,链表长度达到8的话,有可能树化操作
static final int TREEIFY_THRESHOLD = 8;
* 红黑树转化为链表的阈值
static final int UNTREEIFY_THRESHOLD = 6;
* 联合TREEIFY_THRESHOLD 控制桶位,是否树化,只有当table数组长度达到64
* 且 某个桶位 中的链表长度达到8,才会真正树化
static final int MIN_TREEIFY_CAPACITY = 64;
* 线程迁移数据最小步长,控制线程迁移任务最小区间的一个值
private static final int MIN_TRANSFER_STRIDE = 16;
* 扩容相关,计算扩容时生成的一个 表示戳
private static int RESIZE_STAMP_BITS = 16;
* 2^16-1=65535 并发扩容最多线程数
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
* 扩容相关
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;
// 当node节点的 hash值 为 -1 时,表示当前节点是FWD节点
static final int MOVED = -1; // hash for forwarding nodes
// 当node节点的 hash值 为 -2 时,表示当前节点已经树化,且当前节点为TreeBin对象,TreeBin对象代理操作红黑树
static final int TREEBIN = -2; // hash for roots of trees
static final int RESERVED = -3; // hash for transient reservations
// 二进制:31个 1 可以将一个负数通过位运算后 得到正数 但不是取绝对值
static final int HASH_BITS = 0x7fffffff; // usable bits of normal node hash
* 当前系统的CPU数量
static final int NCPU = Runtime.getRuntime().availableProcessors();
LongAdder 变量:
不了解LongAdder,可以看这篇文章:
* 散列表,长度一定是2次方数
transient volatile Node<K,V>[] table;
* 扩容过程中,会将扩容中的新table 赋值给 nextTable 保持引用,扩容结束之后,这里会被设置为Null
private transient volatile Node<K,V>[] nextTable;
* LongAdder 中的 baseCount 为发生竞争时, 或者 当前LongAdder 处于加锁状态时,增量累加到baseCount
private transient volatile long baseCount;
* LongAdder中的cellsBusy 0表示无锁,1表示加锁状态
private transient volatile int cellsBusy;
* LongAdder 中的cekks数组,当baseCount 发生竞争后,会创建cells数组
* 线程会通过计算hash值 取道自己的cell,将增量累加到指定cell中
* 总数 = sum(cells) + baseCount
private transient volatile CounterCell[] counterCells;
* sizeCtl < 0
* 1. -1 表示当前table正在初始化(有线程在创建table数组),当前线程需要自旋等待..
* 2. 表示当前mao正在进行扩容 高16位表示:扩容的标识戳 低16位表示:(1 + nThread) 当前参与并发扩容的线程数量
*
* sizeCtl = 0
* 表示创建table数组时,使用 DEFAULT_CAPACITY 为大小
*
* sizeCtl > 0
* 1.如果table 未初始化,表示初始化大小
* 2.如果已经初始化,表示下次扩容时的 触发条件(阈值)
private transient volatile int sizeCtl;
* 扩容过程中,记录当前进度,所有线程都需要从transferIndex 中分配区间任务,去执行自己的任务
private transient volatile int transferIndex;
静态变量和静态代码块:
// Unsafe mechanics
private static final sun.misc.Unsafe U;
/** 表示sizeCtl 属性在 ConcurrentHashMap 中内存偏移地址 */
private static final long SIZECTL;
/** 表示transferIndex 属性在ConcurrentHashMap 中内存偏移地址 */
private static final long TRANSFERINDEX;
/** 表示baseCount属性在ConcurrentHashMap 中内存偏移地址 */
private static final long BASECOUNT;
/** 表示cellsBusy属性在ConcurrentHashMap 中内存偏移地址 */
private static final long CELLSBUSY;
/** 表示cellValue属性在ConcurrentHashMap 中内存偏移地址 */
private static final long CELLVALUE;
/** 表示数组第一个元素的偏移地址 */
private static final long ABASE;
private static final int ASHIFT;
static {
try {
U = sun.misc.Unsafe.getUnsafe();
Class<?> k = ConcurrentHashMap.class;
SIZECTL = U.objectFieldOffset
(k.getDeclaredField("sizeCtl"));
TRANSFERINDEX = U.objectFieldOffset
(k.getDeclaredField("transferIndex"));
BASECOUNT = U.objectFieldOffset
(k.getDeclaredField("baseCount"));
CELLSBUSY = U.objectFieldOffset
(k.getDeclaredField("cellsBusy"));
Class<?> ck = CounterCell.class;
CELLVALUE = U.objectFieldOffset
(ck.getDeclaredField("value"));
Class<?> ak = Node[].class;
ABASE = U.arrayBaseOffset(ak);
// 表示数组单元 所占用空间大小,scale 表示Node[] 数组中 每一个单元所占用空间大小
int scale = U.arrayIndexScale(ak);
//如果不是2的次方数就报错 1 0000 & 0 1111 = 0
if ((scale & (scale - 1)) != 0)
throw new Error("data type scale not a power of two");
//numberOfLeadingZeros() 返回当前数值的二进制,从高位到地位开始统计,看有多少个0 连续在一起
// 4 => 100 numberOfLeadingZeros(4) = 32-3=29
// ASHIFT = 31 - 29 = 2 ??
// 拿下标为5的
// ABASE + 5 * ASHIFT
// ABASE + (5 << ASHIFT) = ABASE + 5*(1<<ASHIFT) 就是5*4
ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
} catch (Exception e) {
throw new Error(e);
}
}
2、部分常见方法
1、Spread(int h)
* 让高16位也参与运算
* 1100 0011 1010 0101 0001 1100 0001 1110
* 0000 0000 0000 0000 1100 0011 1010 0101
* ---- ---- ---- ---- ---- ---- ---- ---- ^
* 1100 0011 1010 0101 1101 1111 1011 1011 = (h ^ (h >>> 16))
* 0111 1111 1111 1111 1111 1111 1111 1111 = HASH_BITS
* ---- ---- ---- ---- ---- ---- ---- ---- &
* 0100 0011 1010 0101 1101 1111 1011 1011
*/
static final int spread(int h) {
return (h ^ (h >>> 16)) & HASH_BITS;
}
2、tabAt,casTabAt,setTabAt
**
* scale代表一个单位长度 如:int = 4 则 scale = 4
* ASHIFT = 31 - Integer.numberOfLeadingZeros(scale);
* ASHIFT = 31 - (32 - 4) = 31 - 29 = 2
* 相当于:
* i << ASHIFT = i * 4
* 位操作 替代 乘法
*
* ABASE:表示数组第一个元素的偏移地址
*/
@SuppressWarnings("unchecked")
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);
}
**@param tab 代表数组
* @param i 代表位置
* @param c 类似于期望值
* @param v 要把 c 设置成v ,v为新值
* 如果 (i << ASHIFT) + ABASE) ,该位置上的值和期望值 c 一样就替换成 v,返回true
*
*/
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);
}
**@param tab 代表数组
* @param i 代表位置
* @param 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);
}
3、resizeStamp 扩容版本戳
* 16 -> 32
* numberOfLeadingZeros(16) => 1 0000 = 32 - 5 = 27
* 0000 0000 0001 1011
* | ---- ---- ---- ----
* 1 << (16 - 1) = 1000 0000 0000 0000 => 32768
* 1000 0000 0001 1011 : 扩容版本戳
* 扩容版本戳
*/
static final int resizeStamp(int n) {
return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1));
}
4、tableSizeFor
* 返回比 c 大的 最小2的次方数
* c = 6 返回 8
* c = 9 返回 16
*/
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;
}
5、构造方法
* 延迟创建 只有写入数据的时候才会创建 table
*/
public ConcurrentHashMap() {
}
* 如果传入的值是16:
* tableSizeFor(16 + 8 + 1 = 25) = 32
*
* sizeCtl > 0
* 当目前的table未初始化时,sizeCtl 表示初始化容量
*/
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;
}
public ConcurrentHashMap(Map<? extends K, ? extends V> m) {
this.sizeCtl = DEFAULT_CAPACITY;
putAll(m);
}
* initialCapacity:初始化容量
* loadFactor:加载因子
* concurrencyLevel:隔离级别
*
* 注:loadFactor 并没有设置到 LOAD_FACTOR (final 修饰)
* private static final float LOAD_FACTOR = 0.75f
*
* 传入的两个参数 loadFactor 和 concurrencyLevel 只有计算初始化容量时有点作用,
* 其它都没有用到,不会改变到默认的值
*/
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
/** 假如initialCapacity = 16 , loadFactor = 0.75
* size = 1 + 16 / 0.75 = 1 + 21.333 = 22.333
*
* cap = 32 设置初始化容量 sizeCtl
*/
long size = (long)(1.0 + (long)initialCapacity / loadFactor);
int cap = (size >= (long)MAXIMUM_CAPACITY) ?
MAXIMUM_CAPACITY : tableSizeFor((int)size);
this.sizeCtl = cap;
}