ConcurrentHashMap-属性解释

代表hashmap最大能存这么多个键值对
高两位目的是为了控制?知道的评论区说下

private static final int MAXIMUM_CAPACITY = 1 << 30;

代表hashmap默认容量

private static final int DEFAULT_CAPACITY = 16;

数组的最大长度

static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

默认的并行度,为了兼容jdk1.7版本的Segment

private static final int DEFAULT_CONCURRENCY_LEVEL = 16

扩容因子,当元素个数达到0.75*数组长度的时候触发扩容,也是jdk1.7使用,jdk1.8直接使用位操作计算出扩容阈值

private static final float LOAD_FACTOR = 0.75f;

树化阈值,当链表长度达到8的时候树化成红黑树,细致来说是这样
当链表长度达到8并且数组长度<64此时只会扩容,
当链表长度达到8并且数组长度>=64才会树化成红黑树

static final int TREEIFY_THRESHOLD = 8;

这个64就是上面的64

static final int MIN_TREEIFY_CAPACITY = 64;

链化阈值,当红黑树元素个数<=6时退化成链表

static final int UNTREEIFY_THRESHOLD = 6;

并发扩容时每个线程最少处理16个桶

private static final int MIN_TRANSFER_STRIDE = 16;

这三个常量控制并发扩容的结束,其实就是开始扩容的时候设置sizeCtl成一个负数,每次加入一个线程去扩容就sizeCtl++,每次一个线程扩容结束就sizeCtl–, 那么当所有线程扩容结束后sizeCtl就等于最开始设置的那个负数

private static int RESIZE_STAMP_BITS = 16;
private static final int MAX_RESIZERS = (1 << (32 - RESIZE_STAMP_BITS)) - 1;
private static final int RESIZE_STAMP_SHIFT = 32 - RESIZE_STAMP_BITS;

标识为Forwarding节点(hash=-1),当扩容的时候发现桶为null时,会casTab(fwd,i)
当其他线程去执行put操作时,发现定位到的桶是fwd时,就会加入扩容大军,帮助扩容

static final int MOVED     = -1;

标识桶为红黑树(hash=-2)

static final int TREEBIN   = -2;

标识为ReservationNode节点(hash=-3),用于map.compute操作

static final int RESERVED  = -3; 

我解释一下该节点,map.put操作如下
ConcurrentHashMap-属性解释_hashmapmap.compute(“e”,(x,y)->x+" compute "+y); 如下
ConcurrentHashMap-属性解释_并发编程_02
为什么他要加sync锁呢?知道的评论区说下

设想如果没有ReservationNode节点,就没办法使用锁,他就只能写类似下面的代码,而apply方法是非线程安全的,多线程同时compute进来的话会导致不安全

if ((val = remappingFunction.apply(key, null)) != null) {
	node = new Node<K,V>(h, key, val, null);
	casTabAt(tab, i, null, node)
}

普通链表节点,hash为正数

static final int HASH_BITS = 0x7fffffff; 

cpu核心数

static final int NCPU = Runtime.getRuntime().availableProcessors();

hash桶

transient volatile Node<K,V>[] table;

扩容时产生的新数组=2倍大小table

private transient volatile Node<K,V>[] nextTable;

建议去看我的博客 LongAdder

private transient volatile long baseCount;
private transient volatile int cellsBusy;
private transient volatile CounterCell[] counterCells;

这个举例吧,涉及到并发扩容的东西了

private transient volatile int transferIndex;

如图,transferIndex表示当前线程应该迁移桶的最大下标+1
比如线程1应该迁移桶3,桶2,那么transferIndex=4
线程1应该迁移桶1,桶0,那么transferIndex=2
ConcurrentHashMap-属性解释_并发扩容_03

private transient volatile int sizeCtl;

1:如果使用带参initCapacity构造器,sizeCtl就是计算出来的容量
ConcurrentHashMap-属性解释_并发编程_04
2:初始化table时取值-1
ConcurrentHashMap-属性解释_hashmap_05
3:初始化完成设置为0.75*n,扩容阈值

4:第一条线程扩容设置成一个负数,一定是负数

U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)

5:后续每加入一个扩容线程sizeCtl++

U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)

6:后续每个扩容线程结束sizeCtl–

U.compareAndSwapInt(this, SIZECTL, sc = sizeCtl, sc - 1)