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操作如下
map.compute(“e”,(x,y)->x+" compute "+y); 如下
为什么他要加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
private transient volatile int sizeCtl;
1:如果使用带参initCapacity构造器,sizeCtl就是计算出来的容量
2:初始化table时取值-1
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)