首页 > 编程语言 >[Java并发]Concurrenthashmap的size()

[Java并发]Concurrenthashmap的size()

时间:2024-09-10 20:03:40浏览次数:1  
标签:Concurrenthashmap Java rs int long CounterCell 一致性 size

1.一致性定义
关于一致性的定义,大概如下:
一致性(Consistency)是指多副本(Replications)问题中的数据一致性。可以分为强一致性、顺序一致性与弱一致性。
1.1 强一致性(Strict Consistency)
强一致性也被可以被称做:
原子一致性(Atomic Consistency)
线性一致性(Linearizable Consistency)
要满足强一致性,必须符合以下两个要求:

任何一次读都能读到某个数据的最近一次写的数据。
系统中的所有进程,看到的操作顺序,都和全局时钟下的顺序一致。

上述定义用通俗的话来解释就是,假定对同一个数据集合,分别有两个线程A、B进行操作,假定A首先进行的修改操作,那么从时序上在A这个操作之后发生的所有B的操作都应该能看到A修改操作的结果。
1.2 弱一致性
数据更新之后,如果能容忍访问不到或者只能部分访问的情况,就是弱一致性。最终一致性是弱一致性的一个特例。
也就是说,对于数据集,分别有两个线程A、B进行操作,假定A首先进行了修改操作,那么可能从时许上滞后的B进行的读取操作在一段时间内还读取不到这个结果。读取的还是A操作之前的结果。这就是弱一致性。
最终一致性就是说,只要A、B的都不进行任何更新操作,一段时间之后,数据都能读取到最新的数据。
2.size方法源码
2.1 jdk1.8实现
2.1.1 size方法
我们来看看1.8版本中的ConcurrnetHashMap中size方法的源码:

/**
 * {@inheritDoc}
 */
public int size() {
    long n = sumCount();
    return ((n < 0L) ? 0 :
            (n > (long)Integer.MAX_VALUE) ? Integer.MAX_VALUE :
            (int)n);
}

2.1.2 sumCount
实际上底层调用的是sumCount方法:

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;
}

可以看到,这个count,实际上是对CounterCell数组进行遍历,中间没有任何锁操作。
2.1.3 CounterCell
CounterCell源码如下:

/**
 * 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; }
}

这实际上就是一个volatile修饰的计数器。除了Contended这个注解之外,没有什么特别之处,在put、remove的时候,对这个计数器进行增减。
Contended这个注解我们在后面再来详细解释。
counterCells这个数组,实际上size和table一致,这样Counter中的value就是这个数组中index对应到table中bucket的长度。
在table扩容的时候,这个计数器数组也会扩容:
CounterCell[] rs = new CounterCell[n << 1];

2.1.4 addCount

那么在put和remove以及clear等方式对size数量有影响的方法中,都会调用addCount对size进行增减。
x为正数表示增加,负数表示减小。同时check如果大于0则需要对结果进行check,避免在并发过程中由于并发操作带来的计算不准确。

private final void addCount(long x, int check) {
    CounterCell[] as; long b, s;
    //判断是否为空
    if ((as = counterCells) != null ||
        !U.compareAndSwapLong(this, BASECOUNT, b = baseCount, s = b + x)) {
        CounterCell a; long v; int m;
        boolean uncontended = true;
        if (as == null || (m = as.length - 1) < 0 ||
        //a是计算出来的槽位
            (a = as[ThreadLocalRandom.getProbe() & m]) == null ||
            !(uncontended =
            //cas方式
              U.compareAndSwapLong(a, CELLVALUE, v = a.value, v + x))) {
              //增加
            fullAddCount(x, uncontended);
            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) {
                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);
            s = sumCount();
        }
    }
}

2.1.5 fullAddCount
这是执行增加的核心方法,其中大量使用了cas操作,另外还必须考虑到执行的并行性。
ini 代码解读复制代码// See LongAdder version for explanation
private final void fullAddCount(long x, boolean wasUncontended) {
int h;
if ((h = ThreadLocalRandom.getProbe()) == 0) {
ThreadLocalRandom.localInit(); // force initialization
h = ThreadLocalRandom.getProbe();
wasUncontended = true;
}
boolean collide = false; // True if last slot nonempty
//死循环,cas方式修改
for (;

标签:Concurrenthashmap,Java,rs,int,long,CounterCell,一致性,size
From: https://www.cnblogs.com/DCFV/p/18407064

相关文章

  • java----双列集合(Map)
    来看一下双列集合。我们知道,单列集合就是一次只能添加一个元素:单列集合: 双列集合一次可以存储两个值:一、双列集合特点:    1.双列集合一次需要存一对数据,分别为键和值    2.键不能重复,值可以重复    3.键和值是一一对应的,每个键只能找到......
  • JAVA —— IO流续
    复习一下上次的字节流 一、字符流1.Reader——FileReader 文件字符输入流——读字符数据进来2.Writer——FileWriter文件字符输出流——写字符数据进去  1.Reader——FileReader(文件字符输入流) publicstaticvoidmain(String[]args){ try( ......
  • Java中的修饰符
    1.访问权限修饰符访问权限分四种级别:公开级别:用public修饰,对外公开。受保护级别:用protected修饰,向子类及同一个包中的类公开。默认级别:没有访问权限修饰符,向同一个包中的类公开。私有级别:用private修饰,只有类本身可以访问,不对外公开。下图总结访问级别访问权限修饰符同......
  • Java 反射机制
    目录一.概述二.反射的特点三.反射主要应用四.反射注意事项五.代码示例详解1.解析图:2.获取字节码对象3.通过反射获取构造函数并创建对象4.通过反射获取对象字段信息5.通过反射获取方法并执行6.通过反射获取注解信息六.总结1.获取Class字节码对象方式......
  • Java 设计模式-状态模式
    目录一.概述二.主要角色三.代码示例四.优缺点优点:缺点:五.常见应用场景一.概述        状态模式是一种行为设计模式,它允许一个对象在其内部状态改变时改变它的行为。对象看起来好像修改了它的类。状态模式把所有的与一个特定的状态相关的行为放到一个类......
  • Java 设计模式-代理模式
    目录概述一.什么是代理模式1.举例说明二.代理模式作用1.保护代理2.增强功能3.代理交互4.远程代理:三.代理模式3个角色四.静态代理1.代码示例:五.JDK动态代理1.代码示例:六.CGLIB动态代理1.代码示例 七.JDK动态代理和CGLIB动态代理区别八.两种在......
  • [设计模式] Cola-StateMachine : 一个轻量实用的Java状态机框架
    1概述:状态机1.0状态机vs工作流在介绍状态机之前,先介绍一个工作流(WorkFlow),初学者通常容易将两个概念混淆。工作流(WorkFlow),大体是指业务过程(整体或者部分)在计算机应用环境下的自动化,是对工作流程及其各操作步骤之间业务规则的描述。在计算机系统中,工作流属于计算机支持的......
  • Dagger:Android 和 Java 的快速依赖注入框架
    在软件开发中,依赖注入(DI)是一种设计模式,用于实现控制反转,减少代码耦合,提高模块化。Dagger是一个由Google开发的依赖注入库,专门用于Android和Java应用程序,以其快速和高效著称。文章目录......
  • 力扣474-一和零(Java详细题解)
    题目链接:474.一和零-力扣(LeetCode)前情提要:因为本人最近都来刷dp类的题目所以该题就默认用dp方法来做。最近刚学完01背包,所以现在的题解都是以01背包问题为基础再来写的。如果大家不懂01背包的话,建议可以去学一学,01背包问题可以说是背包问题的基础。如果大家感兴趣,......
  • 06JAVA第一次考试选择
    判断正确1,不含公共类的JAVA源文件名称可以随便命名,不受任何限制。2,编译当前路径下的HelloWorld.java文件,使用的命令是:javacHelloWorld.java。3,JamesGosling是Java语言的创始人之一。4,Java语言的标识符区分大小写。5,java.lang包的Character类的isJavaIdentifierStart方......