ConcurrentHashMap中sizeCtl的说明
ConcurrentHashMap设计很强,其中sizeCtl设计十分巧妙。但是在网上查资料真的是以讹传讹啊。所以,我来写一下sizeCtl的说明。
sizeCtl有多重含义,其中除了扩容的时候难理解外,其他的比较好理解
· 1 如果一个ConcurrentHashMap正在初始化,值为-1
2 ConcurrentHashMap初始化完成正在使用,置为size * 0.75
3 扩容的时候,是一个负值
我们今天就来看看这个扩容的时候的值是怎么来的
private final void tryPresize(int size) { int c = (size >= (MAXIMUM_CAPACITY >>> 1)) ? MAXIMUM_CAPACITY : tableSizeFor(size + (size >>> 1) + 1); int sc; while ((sc = sizeCtl) >= 0) { Node<K,V>[] tab = table; int n; if (tab == null || (n = tab.length) == 0) { n = (sc > c) ? sc : c; if (U.compareAndSwapInt(this, SIZECTL, sc, -1)) { try { if (table == tab) { @SuppressWarnings("unchecked") Node<K,V>[] nt = (Node<K,V>[])new Node<?,?>[n]; table = nt; sc = n - (n >>> 2); } } finally { sizeCtl = sc; } } } else if (c <= sc || n >= MAXIMUM_CAPACITY) break; else if (tab == table) { int rs = resizeStamp(n); if (sc < 0) { Node<K,V>[] nt; if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 || sc == rs + MAX_RESIZERS || (nt = nextTable) == null || transferIndex <= 0) break; if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1)) transfer(tab, nt); } else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); } } }
如果第一个线程触发了扩容,那么注意这句话
U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)
rs 的值是怎么来的呢
int rs = resizeStamp(n);
从方法名上看,这个值叫扩容标识戳。
我们就以n=16为例,
static final int resizeStamp(int n) { return Integer.numberOfLeadingZeros(n) | (1 << (RESIZE_STAMP_BITS - 1)); }
Integer.numberOfLeadingZeros(n)就不分析具体实现了,该方法的含义是一个数的最高位第一个非0开始算起,到他的最高位有多少个0。
比如n=16,那么 Integer.numberOfLeadingZeros(n)返回值就是27.
因为16是 10000。一个int是32位,32-5就是27.
(1 << (RESIZE_STAMP_BITS - 1)),其中 RESIZE_STAMP_BITS = 16。 也就是说该表达式的值是一个1,后面跟15个0.这里请注意,这个1的事情。
然后呢,两个值做或运算,那么刚才的1就会让这个返回的int值的低16位中的第 16位是1.
我们再看
U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)
(rs << RESIZE_STAMP_SHIFT) + 2)
rs左移16位,还记得刚才说的1嘛,最高位是符号位,所以这个事肯定是一个负数。同时左移16位后,低16位就全部是0。然后加2.那么不看高16位,低16位的值就是2。
这也就是sizeCtl低16位表示的是参与扩容的线程数 + 1。
当最后一个扩容线程准备收尾的判断依据也是 sizeCtl-1 后是不是等于1。
以上就是sizeCtl的意义,总结下就是高16位是扩容标识戳,低16位是扩容线程数+1。
posted on 2020-10-19 10:44 MaXianZhe 阅读(1370) 评论(0) 编辑 收藏 举报