本文所有的源码都是基于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()方法简单解释的更多相关文章

  1. ConcurrentHashMap中的putIfAbsent方法的使用以及返回值的含义

    public V putIfAbsent(@NotNull K key, @NotNull V value) putIfAbsent方法主要是在向ConcurrentHashMap中添加键—值对的时候 ...

  2. java.util.ComparableTimSort中的sort()方法简单分析

    TimSort算法是一种起源于归并排序和插入排序的混合排序算法,设计初衷是为了在真实世界中的各种数据中能够有较好的性能. 该算法最初是由Tim Peters于2002年在Python语言中提出的. T ...

  3. Java中的wait方法 简单介绍。

    一 wait方法怎么用? package com.aaa.threaddemo; /* * 多线程中的wait方法? public final void wait() throws Interrupt ...

  4. Java中String.valueOf()方法的解释

    1. 由 基本数据型态转换成 String String 类别中已经提供了将基本数据型态转换成 String 的 static 方法 也就是 String.valueOf() 这个参数多载的方法 有下 ...

  5. js中Object.defineProperty()方法的解释

    菜菜: “老大,那个, Object.defineProperty 是什么鬼?” 假设我们有个对象 user ; 我们要给它增加一个属性 name , 我们会这么做 1 2 3 var user = ...

  6. Unity3D中的函数方法及解释

    一.刷新函数 Update 当MonoBehaviour启用时,其Update在每一帧被调用. LateUpdate 当Behaviour启用时,其LateUpdate在每一帧被调用. FixedUp ...

  7. Unity3D中的函数方法和解释

    一.刷新函数 Update 当MonoBehaviour启用时,其Update在每一帧被调用. LateUpdate 当Behaviour启用时,其LateUpdate在每一帧被调用. FixedUp ...

  8. linux中grep使用方法具体解释

    查找特定字符串并颜色显示 [root@fwq test]# grep -n 'the' regular_express.txt --color=auto 8:I can't finish the te ...

  9. ConcurrentHashMap中节点数目并发统计的实现原理

    前言: 前段时间又看了一遍ConcurrentHashMap的源码,对该并发容器的底层实现原理有了更进一步的了解,本想写一篇关于ConcurrentHashMap的put方法所涉及的初始化以及扩容操作 ...

随机推荐

  1. SQL Prompt 破解之道

    1. 下载SQL Prompt 5.3.4.1,是个压缩包,里面有三个文件 免登录免积分下载地址:http://download.csdn.net/detail/caizz520/4557385 1) ...

  2. zTree-已勾选项id传输到action的解决方案

    测试jsp <%@ page language="java" contentType="text/html; charset=utf-8"    page ...

  3. Ajax的方法和使用代码

    //1.创建Ajax对象,已经封装好的函数: function createXHR(){ if (typeof XMLHttpRequest != "undefined"){ re ...

  4. (转)java.util.Scanner应用详解

    java.util.Scanner应用详解   java.util.Scanner是Java5的新特征,主要功能是简化文本扫描.这个类最实用的地方表现在获取控制台输入,其他的功能都很鸡肋,尽管Java ...

  5. Flunetd 用于统一日志记录层的开源数据收集器

    传统的日志查看方式 使用fluentd之后 一.介绍 Fluentd是一个开源的数据收集器,可以统一对数据收集和消费,以便更好地使用和理解数据. 几大特色: 使用JSON统一记录 简单灵活可插拔架构 ...

  6. jQuery源码分析-03扩展工具函数jQuery.extend

    // 扩展工具函数 jQuery.extend({ // http://www.w3school.com.cn/jquery/core_noconflict.asp // 释放$的 jQuery 控制 ...

  7. 百度百科Tooltip的实现--原生js的应用

    我们在浏览百度百科时,不难发现提示框的存在,如下图: 实现如下: 1.HTML代码部分 <!DOCTYPE html><html lang="en">< ...

  8. 超强、超详细Redis数据库入门教程(转载)

    这篇文章主要介绍了超强.超详细Redis入门教程,本文详细介绍了Redis数据库各个方面的知识,需要的朋友可以参考下   [本教程目录] 1.redis是什么 2.redis的作者何许人也 3.谁在使 ...

  9. vuex状态管理,state,getters,mutations,actons的简单使用(一)

    之前的文章中讲过,组件之间的通讯我们可以用$children.$parent.$refs.props.data... 但问题来了,假如项目特别大,组件之间的通讯可能会变得十分复杂... 这个时候了我们 ...

  10. dfs+剪枝:poj2362

    贴题目 Square Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 24604   Accepted: 8449 Descr ...