java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > Java LongAdder使用

Java LongAdder使用与应用实战

作者:IT橘子皮

LongAdder是 Java 并发编程中为高并发计数而生的利器,本文就来详细的介绍一下Java LongAdder的使用,具有一定的参考价值,感兴趣的可以了解一下

LongAdder是 Java 并发编程中为高并发计数而生的利器。下面这张表格能帮助你快速把握其全貌,之后我们再深入细节和实战。

特性维度说明
设计目标高并发场景下的高性能计数/累加操作,优化多线程写竞争 。
核心思想分而治之,分散热点​:将单一变量的竞争压力分散到多个单元(base+ Cell[]),以空间换取时间 。
关键数据结构base(基础值) 和 Cell[](单元数组) 。
写入流程1. 无竞争或低竞争时,CAS 操作直接更新 base。 2. 竞争激烈时,线程通过哈希映射到 Cell[]中的某个单元进行更新,大幅减少冲突 。
读取流程 (sum())​返回 base与所有 Cell单元值的累加和。此操作不保证强一致性,是最终一致性的,因为在求和过程中可能有其他线程正在更新 。
主要优点高并发写入性能远超 AtomicLong,有效减少 CAS 空自旋,避免高竞争下的性能骤降 。
主要缺点更高的内存消耗;读取操作 (sum()) 非原子快照,是最终一致性的;不支持 compareAndSet等原子条件更新操作 。
典型应用场景高频统计计数器(如 API 调用次数、点击量)、监控指标收集、频率统计等“写多读少”且对读的实时精确性要求不高的场景 。

💡 深入核心原理

要理解 LongAdder的高性能,需要深入其内部机制。

🛠️ 实战应用示例

LongAdder非常适用于以下场景:

  1. API 请求统计与监控

    可以轻松统计服务的请求量、成功/失败次数、总耗时等指标 。

    public class ApiRequestMonitor {
        private final LongAdder requestCount = new LongAdder();
        private final LongAdder totalLatency = new LongAdder();
    
        public void recordRequest(long latency) {
            requestCount.increment();
            totalLatency.add(latency);
        }
    
        public MonitoringSnapshot getSnapshot() {
            // 注意:sum() 获取的是瞬时近似值
            return new MonitoringSnapshot(requestCount.sum(), totalLatency.sum());
        }
    }
    
  2. 结合 ConcurrentHashMap 进行频率统计

    这是一种常见且高效的模式,用于统计元素出现次数 。

    ConcurrentMap<String, LongAdder> freqMap = new ConcurrentHashMap<>();
    
    public void count(String word) {
        // 如果键不存在,则原子性地放入一个新的 LongAdder
        freqMap.computeIfAbsent(word, k -> new LongAdder())
               .increment(); // 然后递增
    }
    // 获取某个词的频率
    long frequency = freqMap.getOrDefault(word, new LongAdder()).sum();
    

⚠️ 使用注意事项与最佳实践

  1. 理解一致性语义​:LongAddersum()方法是最终一致性的。如果你的业务场景要求在任何时刻读取都必须是完全精确的值(例如金融账户余额),那么 AtomicLong或锁机制更为合适 。
  2. 关注内存占用​:Cell数组和避免伪共享的填充会带来比 AtomicLong更高的内存开销。在内存受限或并发度不高的环境中,需要权衡利弊 。
  3. 避免频繁调用 sum() ​:sum()方法需要遍历 Cell数组,在数组较大时有一定开销。应避免在性能关键路径中频繁调用 。
  4. 重置操作​:reset()方法将 base和所有 Cell置零,但此操作非原子性。通常仅在确定没有并发更新时(如一个统计周期结束清零时)使用 。

🔄 选型指南:LongAdder vs. AtomicLong

场景推荐选择理由
极高并发写入,对读的实时精确性要求不高​(如统计、监控)LongAdder写吞吐量极高,通过分散竞争避免性能瓶颈 。
低并发环境,或需要频繁读取精确瞬时值​(如序列号生成、状态标志)AtomicLong读取 (get()) 是强一致性的单次 volatile 读,性能极高;接口丰富,支持 compareAndSet等复杂原子操作 。
需要复杂的累加操作​(如求最大值、最小值)LongAccumulatorLongAdderLongAccumulator的一个特例(专用于加法)。LongAccumulator允许传入自定义二元运算符,功能更灵活 。

💎 总结

LongAdder是 Java 并发工具包中“分而治之”思想的杰出代表。它通过空间换时间,巧妙地化解了高并发下的写入竞争,在统计、监控等“写多读少”的场景下表现卓越。

选择 LongAdder的关键在于明确:​你是否愿意用读取操作的强一致性和更高的内存开销,来换取极高的并发写入性能

到此这篇关于Java LongAdder使用与应用实战的文章就介绍到这了,更多相关Java LongAdder使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文