2020年4月23日晚上,阿里交叉面时,面试官问到了ConcurrentHashMap中size()方法的实现原理,我瞬间懵逼,因为之前只关注了put和get等操作,压根根本没看过size()的源码…
size方法
1 | public int size() { |
1.sumCount()的源码如下:
1 | final long sumCount() { |
2.CounterCell的定义如下:
1 | static final class CounterCell { .internal.vm.annotation.Contended |
3.声明CounterCell[] cs = counterCells;
变量的源码如下:
1 | private transient volatile CounterCell[] counterCells; |
4.声明long sum = baseCount;
变量的源码如下:
1 | // 记录了哈希表中的元素个数 |
mappingCount方法
在JDK 8新增了mappingCount()方法也是用于求键值对的个数,但是其返回值为long。设计者们建议我们使用该方法代替size()方法。
虽然哈希表的长度不会超过int的存储范围,但是每个桶中可以有多个元素,从而导致ConcurrentHashMap中的键值对个数有可能会超出int的存储范围。
此外,mappingCount()返回的是一个估计值,当存在并发插入和删除时,实际的键值对个数可能会有所不同。
1 | public long mappingCount() { |
addCount方法
1 | // 当执行插入或者删除操作时,将调用addCount修改baseCount的值 |
1.初始化时,数组counterCells为空。当多个线程并发地执行修改操作时,通过CAS操作修改baseCount的值。
2.如果修改baseCount的CAS操作失败,则使用CAS操作修改counterCells中的值。
3.如果counterCells为空,或者修改counterCells的CAS操作失败,则执行fullAddCount方法。
4.fullAddCount方法的核心操作如下:
1)通过CAS操作修改cellsBusy的值,如果修改成功,则初始化counterCells。
2)如果修改cellsBusy的CAS操作失败,则继续尝试通过CAS操作修改baseCount的值。
1 | // fullAddCount方法的源码很长,核心代码如下 |
声明cellsBusy变量的源码如下:
1 | // 用于数组counterCells初始化或扩容时 |