本文所有的源码都是基于JDK1.8

ConcurrentHashmap中的size()方法源码:

  1. public int size() {
  2. long n = sumCount();
  3. return ((n < 0L) ? 0 :
  4. (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
  5. (int)n);
  6. }
  7. final long sumCount() {
  8. CounterCell[] as = counterCells; CounterCell a;
  9. long sum = baseCount;
  10. if (as != null) {
  11. for (int i = 0; i < as.length; ++i) {
  12. if ((a = as[i]) != null)
  13. sum += a.value;
  14. }
  15. }
  16. return sum;
  17. }

根据JDK1.8的注解,当你想大致了解ConcurrentHashmap的容器大小时,建议使用mappingCount()方法。源码如下:

  1. /**
  2. * Returns the number of mappings. This method should be used
  3. * instead of {@link #size} because a ConcurrentHashMap may
  4. * contain more mappings than can be represented as an int. The
  5. * value returned is an estimate; the actual count may differ if
  6. * there are concurrent insertions or removals.
  7. *(大致的意思是:返回容器的大小。这个方法应该被用来代替size()方法,因为
  8. * ConcurrentHashMap的容量大小可能会大于int的最大值。
  9. * 返回的值是一个估计值;如果有并发插入或者删除操作,则实际的数量可能有所不同。)
  10. * @return the number of mappings
  11. * @since 1.8
  12. */
  13. public long mappingCount() {
  14. long n = sumCount();
  15. return (n < 0L) ? 0L : n; // ignore transient negative values
  16. }

其实baseCount就是记录容器数量的,直接放回baseCount不就可以了吗?为什么sumCount()方法中还要遍历counterCells数组,累加对象的值呢?

其中:counterCells是个全局的变量,表示的是CounterCell类数组。CounterCell是ConcurrentHashmap的内部类,它就是存储一个值。

  1. /**
  2. * Table of counter cells. When non-null, size is a power of 2.
  3. */
  4. private transient volatile CounterCell[] counterCells;
  1. /**
  2. * A padded cell for distributing counts. Adapted from LongAdder
  3. * and Striped64. See their internal docs for explanation.
  4. */
  5. @sun.misc.Contended static final class CounterCell {
  6. volatile long value;
  7. CounterCell(long x) { value = x; }
  8. }

JDK1.8中使用一个volatile类型的变量baseCount记录元素的个数,当插入新数据put()或则删除数据remove()时,会通过addCount()方法更新baseCount:

  1. private final void addCount(long x, int check) {
  2. CounterCell[] as; long b, s;
  3. //U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x) 每次竟来都baseCount都加1因为x=1
  4. if ((as = counterCells) != null ||
  5. !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {//1
  6. CounterCell a; long v; int m;
  7. boolean uncontended = true;
  8. if (as == null || (m = as.length - 1) < 0 ||
  9. (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
  10. !(uncontended =
  11. U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
  12. //多线程CAS发生失败的时候执行
  13. fullAddCount(x, uncontended);//2
  14. return;
  15. }
  16. if (check <= 1)
  17. return;
  18. s = sumCount();
  19. }
  20. if (check >= 0) {
  21. Node<K,V>[] tab, nt; int n, sc;
  22. //当条件满足开始扩容
  23. while (s >= (long)(sc = sizeCtl) && (tab = table) != null &&
  24. (n = tab.length) < MAXIMUM_CAPACITY) {
  25. int rs = resizeStamp(n);
  26. if (sc < 0) {//如果小于0说明已经有线程在进行扩容操作了
  27. //一下的情况说明已经有在扩容或者多线程进行了扩容,其他线程直接break不要进入扩容操作
  28. if ((sc >>> RESIZE_STAMP_SHIFT) != rs || sc == rs + 1 ||
  29. sc == rs + MAX_RESIZERS || (nt = nextTable) == null ||
  30. transferIndex <= 0)
  31. break;
  32. if (U.compareAndSwapInt(this, SIZECTL, sc, sc + 1))//如果相等说明扩容已经完成,可以继续扩容
  33. transfer(tab, nt);
  34. }
  35. //这个时候sizeCtl已经等于(rs << RESIZE_STAMP_SHIFT) + 2等于一个大的负数,这边加上2很巧妙,因为transfer后面对sizeCtl--操作的时候,最多只能减两次就结束
  36. else if (U.compareAndSwapInt(this, SIZECTL, sc,
  37. (rs << RESIZE_STAMP_SHIFT) + 2))
  38. transfer(tab, null);
  39. s = sumCount();
  40. }
  41. }
  42. }

1、初始化时counterCells为空,在并发量很高时,如果存在两个线程同时执行CAS修改baseCount值,则失败的线程会继续执行方法体中的逻辑,执行fullAddCount(x, uncontended)方法,这个方法其实就是初始化counterCells,并将x的值插入到counterCell类中,而x值一般也就是1,这单可以从put()方法中得知。

  1. final V putVal(K key, V value, boolean onlyIfAbsent) {
  2. ...
  3. //1就是要CAS 更新baseCount的值,binCount代表此链表或树的值,一般都大于0.
  4. addCount(1L, binCount);
  5. return null;
  6. }

所以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. SetConsoleWindowInfo 函数--设置控制台窗口的大小和位置

    SetConsoleWindowInfo函数 来源:https://msdn.microsoft.com/en-us/library/windows/desktop/ms686125(v=vs.85) ...

  2. CSS规则

    CSS规则 --------------------------------------------- 1 前言 2 代码风格 2.1 文件 2.2 缩进 2.3 空格 2.4 行长度 2.5 选择器 ...

  3. Android学习笔记-Adapter基础讲解

    本节引言 从本节开始我们要讲的UI控件都是跟Adapter(适配器)打交道的,了解并学会使用这个Adapter很重要, Adapter是用来帮助填充数据的中间桥梁,简单点说就是:将各种数据以合适的形式 ...

  4. python编码问题一点通

    一.了解字符编码的知识储备 1. 文本编辑器存取文件的原理(nodepad++,pycharm,word) 打开编辑器就打开了启动了一个进程,是在内存中的,所以在编辑器编写的内容也都是存放与内存中的, ...

  5. Linux下C程序的存储空间布局

    一个程序本质上都是由 BSS 段.data段.text段三个组成的.可以看到一个可执行程序在存储(没有调入内存)时分为代码段.数据区和未初始化数据区三部分. BSS段(未初始化数据区):在采用段式内存 ...

  6. ASP.NET MVC 创建 Area 以及使用

    此博客全乘抄袭,只为以后自己能再次用到 参考链接 http://www.cnblogs.com/willick/p/3331519.html ASP.NET MVC允许使用 Area(区域)来组织We ...

  7. JS中几种常见的数组算法(前端面试必看)

    JS中几种常见的数组算法 1.将稀疏数组变成不稀疏数组 /** * 稀疏数组 变为 不稀疏数组 * @params array arr 稀疏数组 * @return array 不稀疏的数组 */ f ...

  8. Java重定向和转发的路径问题

      路径问题:         ①相对路径和绝对路径:             绝对路径:  绝对路径是以/开头的路径!             相对于当前服务器的绝对路径: 如果是服务器解析,那么/ ...

  9. 关于<form:select>

    今天写基于SSM框架的程序,用到了<form:select>.由于一开始遇到了问题,所以在这里加以记录,供以后查看. 直接看jsp页面的代码 <%@ page language=&q ...

  10. easyui1.2.6 validate输入框验证在火狐下的一个bug

    easyui版本1.2.6,其他版本未测试是否有这个问题. 问题描述:FF浏览器中,当前输入框需要失去焦点验证,在编辑完成后不点击其他地方使输入框失去焦点,而直接点击保存按钮,此时只会进行输入框失去焦 ...