当前位置:

首页 > 编程开发 > Java并发编程:探究LongAdder的性能表现

Java并发编程:探究LongAdder的性能表现

longAccumulate方法finalvoidlongAccumulate(longx,LongBinaryOperatorfn,booleanwasUncontended){inth;if((h=getProbe())==0){ThreadLocalRandom.current();//forceinitializationh=getProbe();wasUncontended=true;}booleancollide=false;//Trueiflastslotnonemptyfor(;;){Ce

本文目录

    longAccumulate方法

    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        int h;
        if ((h = getProbe()) == 0) {
            ThreadLocalRandom.current(); // force initialization
            h = getProbe();
            wasUncontended = true;
        }
        boolean collide = false;                // True if last slot nonempty
        for (;;) {
            Cell[] as; Cell a; int n; long v;
            if ((as = cells) != null && (n = as.length) > 0) {
                if ((a = as[(n - 1) & h]) == null) {
                    if (cellsBusy == 0) {       // Try to attach new Cell
                        Cell r = new Cell(x);   // Optimistically create
                        if (cellsBusy == 0 && casCellsBusy()) {
                            boolean created = false;
                            try {               // Recheck under lock
                                Cell[] rs; int m, j;
                                if ((rs = cells) != null &&
                                    (m = rs.length) > 0 &&
                                    rs[j = (m - 1) & h] == null) {
                                    rs[j] = r;
                                    created = true;
                                }
                            } finally {
                                cellsBusy = 0;
                            }
                            if (created)
                                break;
                            continue;           // Slot is now non-empty
                        }
                    }
                    collide = false;
                }
                else if (!wasUncontended)       // CAS already known to fail
                    wasUncontended = true;      // Continue after rehash
                else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                             fn.applyAsLong(v, x))))
                    break;
                else if (n >= NCPU || cells != as)
                    collide = false;            // At max size or stale
                else if (!collide)
                    collide = true;
                else if (cellsBusy == 0 && casCellsBusy()) {
                    try {
                        if (cells == as) {      // Expand table unless stale
                            Cell[] rs = new Cell[n << 1];
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];
                            cells = rs;
                        }
                    } finally {
                        cellsBusy = 0;
                    }
                    collide = false;
                    continue;                   // Retry with expanded table
                }
                h = advanceProbe(h);
            }
            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                boolean init = false;
                try {                           // Initialize table
                    if (cells == as) {
                        Cell[] rs = new Cell[2];
                        rs[h & 1] = new Cell(x);
                        cells = rs;
                        init = true;
                    }
                } finally {
                    cellsBusy = 0;
                }
                if (init)
                    break;
            }
            else if (casBase(v = base, ((fn == null) ? v + x :
                                        fn.applyAsLong(v, x))))
                break;                          // Fall back on using base
        }
    }

    代码较长,我们分段来分析,首先介绍一下各部分的内容

    • 第一部分:for循环之前的代码,主要是获取线程的hash值,如果是0的话就强制初始化

    • 第二部分:for循环中第一个if语句,在Cell数组中进行累加、扩容

    • 第三部分:for循环中第一个else if语句,这部分的作用是创建Cell数组并初始化

    • 第四部分:for循环中第二个else if语句,当Cell数组竞争激烈时尝试在base上进行累加

    线程hash值

    int h; 
    if ((h = getProbe()) == 0) { 
        ThreadLocalRandom.current(); // force initialization 
        h = getProbe(); 
        wasUncontended = true;   // true表示没有竞争
    } 
    boolean collide = false; // True if last slot nonempty 可以理解为是否需要扩容

    这部分的核心代码是getProbe方法,这个方法的作用就是获取线程的hash值,方便后面通过位运算定位到Cell数组中某个位置,如果是0的话就会进行强制初始化

    初始化Cell数组

    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        // 省略...
        for (;;) {
            Cell[] as; Cell a; int n; long v;
            if ((as = cells) != null && (n = as.length) > 0) {
                // 省略...
            }
            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {  // 获取锁
                boolean init = false;  // 初始化标志
                try {                           // Initialize table
                    if (cells == as) {
                        Cell[] rs = new Cell[2];  // 创建Cell数组
                        rs[h & 1] = new Cell(x);  // 索引1位置创建Cell元素,值为x=1
                        cells = rs;   // cells指向新数组
                        init = true;  // 初始化完成
                    }
                } finally {
                    cellsBusy = 0;  // 释放锁
                }
                if (init)
                    break;  // 跳出循环
            }
            else if (casBase(v = base, ((fn == null) ? v + x :
                                        fn.applyAsLong(v, x))))
                break;                          // Fall back on using base
        }
    }

    第一种情况下Cell数组为null,所以会进入第一个else if语句,并且没有其他线程进行操作,所以cellsBusy==0,cells==as也是true,casCellsBusy()尝试对cellsBusy进行cas操作改成1也是true。

    首先创建了一个有两个元素的Cell数组,然后通过线程h & 1 的位运算在索引1的位置设置一个value为1的Cell,然后重新赋值给cells,标记初始化成功,修改cellsBusy为0表示释放锁,最后跳出循环,初始化操作就完成了。

    对base进行累加

    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        // 省略...
        for (;;) {
            Cell[] as; Cell a; int n; long v;
            if ((as = cells) != null && (n = as.length) > 0) {
                // 省略...
            }
            else if (cellsBusy == 0 && cells == as && casCellsBusy()) {
                // 省略...
            }
            else if (casBase(v = base, ((fn == null) ? v + x :
                                        fn.applyAsLong(v, x))))
                break;                          // Fall back on using base
        }
    }

    第二个else if语句的意思是当Cell数组中所有位置竞争都很激烈时,就尝试在base上进行累加,可以理解为最后的保障

    Cell数组初始化之后

    final void longAccumulate(long x, LongBinaryOperator fn,
                              boolean wasUncontended) {
        // 省略...
        for (;;) {
            Cell[] as; Cell a; int n; long v;
            if ((as = cells) != null && (n = as.length) > 0) {  // as初始化之后满足条件
                if ((a = as[(n - 1) & h]) == null) {  // as中某个位置的值为null
                    if (cellsBusy == 0) {       // Try to attach new Cell 是否加锁
                        Cell r = new Cell(x);   // Optimistically create 创建新Cell
                        if (cellsBusy == 0 && casCellsBusy()) { // 双重检查是否有锁,并尝试加锁
                            boolean created = false;  // 
                            try {               // Recheck under lock
                                Cell[] rs; int m, j;
                                if ((rs = cells) != null &&
                                    (m = rs.length) > 0 &&
                                    rs[j = (m - 1) & h] == null) {  // 重新检查该位置是否为null
                                    rs[j] = r;  // 该位置添加Cell元素
                                    created = true;  // 新Cell创建成功
                                }
                            } finally {
                                cellsBusy = 0;  // 释放锁
                            }
                            if (created)
                                break;  // 创建成功,跳出循环
                            continue;           // Slot is now non-empty
                        }
                    }
                    collide = false;  // 扩容标志
                }
                else if (!wasUncontended)       // 上面定位到的索引位置的值不为null
                    wasUncontended = true;      // 重新计算hash,重新定位其他索引位置重试
                else if (a.cas(v = a.value, ((fn == null) ? v + x :
                                             fn.applyAsLong(v, x))))  // 尝试在该索引位置进行累加
                    break;
                else if (n >= NCPU || cells != as)  // 如果数组长度大于等于CPU核心数,就不能在扩容
                    collide = false;            // At max size or stale
                else if (!collide)  // 数组长度没有达到最大值,修改扩容标志可以扩容
                    collide = true;
                else if (cellsBusy == 0 && casCellsBusy()) { // 尝试加锁
                    try {
                        if (cells == as) {      // Expand table unless stale
                            Cell[] rs = new Cell[n << 1];  // 创建一个原来长度2倍的数组
                            for (int i = 0; i < n; ++i)
                                rs[i] = as[i];  // 把原来的元素拷贝到新数组中
                            cells = rs;  // cells指向新数组
                        }
                    } finally {
                        cellsBusy = 0;  // 释放锁
                    }
                    collide = false;  // 已经扩容完成,修改标志不用再扩容
                    continue;                   // Retry with expanded table
                }
                h = advanceProbe(h);  // 重新获取hash值
            }
            // 省略...
    }

    根据代码中的注释分析一遍整体逻辑

    • 首先如果找到数组某个位置上的值为null,说明可以在这个位置进行操作,就创建一个新的Cell并初始化值为1放到这个位置,如果失败了就重新计算hash值再重试

    • 定位到的位置已经有值了,说明线程之间产生了竞争,如果wasUncontended是false就修改为true,并重新计算hash重试

    • 定位的位置有值并且wasUncontended已经是true,就尝试在该位置进行累加

    • 当累加失败时,判断数组容量是否已经达到最大,如果是就不能进行扩容,只能rehash并重试

    • 如果前面条件都不满足,并且扩容标志collide标记为false的话就修改为true,表示可以进行扩容,然后rehash重试

    • 首先尝试加锁,成功了就进行扩容操作,每次扩容长度是之前的2倍,然后把原来数组内容拷贝到新数组,扩容就完成了。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发 Java
    相关文章 更多
    Java单元测试入门:使用JUnit 5编写第一个测试用例
    Java单元测试入门:使用JUnit 5编写第一个测试用例

    面向初学者的JUnit 5实用入门指南。从测试概念、Maven依赖配置,到手把手编写首个单元测试用例及断言验证,助你掌握Java单元测试的核心流程。

    Java变量未初始化报错解析:局部变量与成员变量默认值差异
    Java变量未初始化报错解析:局部变量与成员变量默认值差异

    深入解析Java中局部变量未初始化编译报错与成员变量自动赋予默认值的机制差异,涵盖JVM内存分配、编译期确定性赋值检查与实际排查修复方案。

    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat
    Java获取当前时间并格式化输出:DateTimeFormatter与SimpleDateFormat

    详细讲解在Java中获取当前系统时间并进行格式化输出的两种核心方案:基于Java 8全新日期时间API的DateTimeFormatter与传统遗留的SimpleDateFormat,包含完整代码示例、线程安全机制对比及生产选型建议。

    Java中final与static final常量的定义及初始化规则
    Java中final与static final常量的定义及初始化规则

    深入解析Java中final变量与static final常量的定义、内存语义、初始化时机及合法赋值路径,涵盖编译期常量优化与实战避坑指南。

    Java抽象方法与接口方法的区别及选型建议
    Java抽象方法与接口方法的区别及选型建议

    深入对比Java中抽象类抽象方法与接口方法的语法演进、设计意图及运行期差异,并提供清晰的架构选型决策流程与实战范例。

    Java实现HTTP请求与JSON解析的完整代码示例
    Java实现HTTP请求与JSON解析的完整代码示例

    本文提供Java标准库HttpClient发起HTTP GET/POST请求并结合Jackson进行JSON数据反序列化与序列化的完整代码示例与工程实践指南。

    Java递归出现StackOverflowError的原因分析及栈空间优化方案
    Java递归出现StackOverflowError的原因分析及栈空间优化方案

    详细分析Java递归调用出现StackOverflowError的原因,结合代码说明线程栈、栈帧和递归深度之间的关系,并介绍终止条件排查、迭代改写、减少栈帧开销以及通过-Xss调整JVM栈空间的方法。

    Java空指针异常(NPE)的代码排查与修复实例
    Java空指针异常(NPE)的代码排查与修复实例

    针对Java中常见的NullPointerException(NPE),本文从堆栈日志定位、典型代码触发场景、具体修复代码对比到防御体系进行系统讲解,提供实用且易上手的排查与修复方案。

    selenium上传文件教程本地文件路径设置方法
    selenium上传文件教程本地文件路径设置方法

    深入解析Selenium处理文件上传的技术细节,涵盖标准HTML控件的路径直接注入法与非标准控件的系统级模拟方案,提供Windows与Linux环境下的路径设置最佳实践及常见异常排查。

    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景
    大数据开发语言选型:Java对比Scala与Python的优缺点及适用场景

    针对大数据工程场景,系统对比Java、Scala与Python在执行性能、生态结合、开发效率和维护成本上的核心差异,给出清晰的架构选型依据与落地方案。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。