ConcurrentHashmap中的size()方法简单解释
来源:互联网 发布:牛贝淘宝客3.17开源版 编辑:程序博客网 时间:2024/05/29 07:48
本文所有的源码都是基于JDK1.8
ConcurrentHashmap中的size()方法源码:
public int size() { long n = sumCount(); return ((n < 0L) ? 0 : (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE : (int)n); }final long sumCount() { CounterCell[] as = counterCells; CounterCell a; long sum = baseCount; if (as != null) { for (int i = 0; i < as.length; ++i) { if ((a = as[i]) != null) sum += a.value; } } return sum; }
根据JDK1.8的注解,当你想大致了解ConcurrentHashmap的容器大小时,建议使用mappingCount()方法。源码如下:
/** * Returns the number of mappings. This method should be used * instead of {@link #size} because a ConcurrentHashMap may * contain more mappings than can be represented as an int. The * value returned is an estimate; the actual count may differ if * there are concurrent insertions or removals. *(大致的意思是:返回容器的大小。这个方法应该被用来代替size()方法,因为 * ConcurrentHashMap的容量大小可能会大于int的最大值。 * 返回的值是一个估计值;如果有并发插入或者删除操作,则实际的数量可能有所不同。) * @return the number of mappings * @since 1.8 */ public long mappingCount() { long n = sumCount(); return (n < 0L) ? 0L : n; // ignore transient negative values }
其实baseCount就是记录容器数量的,直接放回baseCount不就可以了吗?为什么sumCount()方法中还要遍历counterCells数组,累加对象的值呢?
其中:counterCells是个全局的变量,表示的是CounterCell类数组。CounterCell是ConcurrentHashmap的内部类,它就是存储一个值。
/** * Table of counter cells. When non-null, size is a power of 2. */ private transient volatile CounterCell[] counterCells;
/** * A padded cell for distributing counts. Adapted from LongAdder * and Striped64. See their internal docs for explanation. */ @sun.misc.Contended static final class CounterCell { volatile long value; CounterCell(long x) { value = x; } }
JDK1.8中使用一个volatile类型的变量baseCount记录元素的个数,当插入新数据put()或则删除数据remove()时,会通过addCount()方法更新baseCount:
private final void addCount(long x, int check) { CounterCell[] as; long b, s; //U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x) 每次竟来都baseCount都加1因为x=1 if ((as = counterCells) != null || !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {//1 CounterCell a; long v; int m; boolean uncontended = true; if (as == null || (m = as.length - 1) < 0 || (a = as[ThreadLocalRandom.getProbe() & m]) == null || !(uncontended = U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) { //多线程CAS发生失败的时候执行 fullAddCount(x, uncontended);//2 return; } if (check <= 1) return; s = sumCount(); } if (check >= 0) { Node<K,V>[] tab, nt; int n, sc; //当条件满足开始扩容 while (s >= (long)(sc = sizeCtl) && (tab = table) != null && (n = tab.length) < MAXIMUM_CAPACITY) { int rs = resizeStamp(n); if (sc < 0) {//如果小于0说明已经有线程在进行扩容操作了 //一下的情况说明已经有在扩容或者多线程进行了扩容,其他线程直接break不要进入扩容操作 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); } //这个时候sizeCtl已经等于(rs << RESIZE_STAMP_SHIFT) + 2等于一个大的负数,这边加上2很巧妙,因为transfer后面对sizeCtl--操作的时候,最多只能减两次就结束 else if (U.compareAndSwapInt(this, SIZECTL, sc, (rs << RESIZE_STAMP_SHIFT) + 2)) transfer(tab, null); s = sumCount(); } }}
1、初始化时counterCells为空,在并发量很高时,如果存在两个线程同时执行CAS修改baseCount值,则失败的线程会继续执行方法体中的逻辑,执行fullAddCount(x, uncontended)方法,这个方法其实就是初始化counterCells,并将x的值插入到counterCell类中,而x值一般也就是1,这单可以从put()方法中得知。
final V putVal(K key, V value, boolean onlyIfAbsent) {...//1就是要CAS 更新baseCount的值,binCount代表此链表或树的值,一般都大于0. addCount(1L, binCount); return null;}
所以counterCells存储的都是value为1的CounterCell对象,而这些对象是因为在CAS更新baseCounter值时,由于高并发而导致失败,最终将值保存到CounterCell中,放到counterCells里。这也就是为什么sumCount()中需要遍历counterCells数组,sum累加CounterCell.value值了。
参考博客:
谈谈 ConcurrentHashMap1.7 和 1.8 的不同实现
- ConcurrentHashmap中的size()方法简单解释
- ConcurrentHashMap解释
- ConcurrentHashMap的size操作
- STL中的size()方法(13)
- ConcurrentHashMap详解以及get方法保持同步的解释
- ConcurrentHashMap详解以及get方法保持同步的解释
- ConcurrentHashMap详解以及get方法保持同步的解释
- ConcurrentHashMap详解以及get方法保持同步的解释
- UIViewController中的一些方法解释
- 关于ConcurrentHashMap的size的思考
- java中的length属性和length()方法和size()方法
- java中的length属性和length()方法和size()方法
- java中的length属性和length()方法和size()方法
- java中的length属性和length()方法和size()方法
- java中的length属性和length()方法和size()方法
- java中的length属性和length()方法和size()方法
- java中的length属性和length()方法和size()方法
- java中的length属性和length()方法和size()方法
- JAVA下实现多线程断点下载
- theano T.col 实例
- 【权威发布】360天眼实验室:Xshell被植入后门代码事件分析报告(完整版)
- centos6-安装sonarsource
- linux学习笔记(5)
- ConcurrentHashmap中的size()方法简单解释
- lstm的原理 详解
- MySQL在Ubuntu系统上的安装配置
- Image Smoother问题及解法
- MIT eecs 6.00 problemset2
- tomcat启动异常
- JAVA8 JDK 对字符串连接的改进
- 快速排序
- 深入分析Java Web技术内幕(修订版) 读书笔记