longadder通过分段累加(cell数组)分散cas竞争,显著提升高并发写性能,但sum()仅提供最终一致性近似值,不满足监控指标强一致性要求,故micrometer counter未直接采用。

Java 中 LongAdder 本身不直接参与 Micrometer 的 Counter 实现,但它的设计思想和适用场景与 Counter 的底层需求高度契合——尤其在高并发写入指标的场景下,理解它有助于规避 Counter 使用中的性能陷阱。
Counter 的底层实现并不依赖 LongAdder
Micrometer 的 Counter(如 PrometheusMeterRegistry 中)默认使用的是 AtomicLong 或其封装(如 DoubleAdder 对应 double 类型),而非 LongAdder。这是因为:
- Counter 的语义是“只增不减”,且 Micrometer 要求指标值具备线性一致性(用于 Prometheus 拉取时的准确快照)
-
LongAdder的sum()方法是非原子的——它返回近似和,可能漏掉正在更新的 cell 值,不满足监控指标对精度的硬性要求 - Prometheus 客户端库(如
simpleclient)在暴露指标时需一次性读取精确值,LongAdder.sum()不保证强一致性
什么时候该考虑 LongAdder?——自定义高并发业务计数器
如果你在业务代码中需要高频、多线程递增一个本地计数器(比如统计某段逻辑每秒执行次数),再由你主动将其同步到 Micrometer Counter,这时 LongAdder 就非常合适:
- 比
AtomicLong.incrementAndGet()在高争用下性能更好(分段累加,减少 CAS 冲突) - 适合“先本地高速累加,再周期性汇总上报”的模式
- 注意:上报时仍需调用
counter.set(longAdder.sum())或counter.increment(delta),不能直接绑定LongAdder实例
示例:
private final LongAdder localCounter = new LongAdder();
private final Counter promCounter = Counter.builder("my.custom.ops")
.register(meterRegistry);
<p>// 高频业务逻辑中
public void doWork() {
localCounter.increment();
}</p><p>// 定时任务(如每秒)同步到 Micrometer
@Scheduled(fixedRate = 1000)
public void flushToPrometheus() {
long delta = localCounter.sumThenReset(); // 原子获取并清零
if (delta > 0) {
promCounter.increment(delta);
}
}</p>
Counter.increment() 在高并发下的实际表现
对于绝大多数 Spring Boot 应用,Micrometer 默认的 AtomicLong- backed Counter 完全够用:
- 单次
increment()是一个 CAS 操作,JVM 层面高度优化 - 实测在万级 QPS 下,CPU 开销可忽略(远低于日志、序列化、网络等环节)
- 真正瓶颈通常不在 Counter 本身,而在指标采集频率、Prometheus 抓取间隔或 /actuator/prometheus 端点响应
只有当你观察到 Counter.increment() 出现明显竞争(如 JFR 中看到大量 Unsafe.compareAndSwapLong 自旋),且已排除其他瓶颈,才需考虑上述 LongAdder + 批量 flush 方案。
替代方案:DistributionSummary 或 Timer 更适合高频事件
如果目标是统计请求量、延迟、大小等高频事件,优先用:
-
DistributionSummary:自动记录计数、总和、分布(P90/P95/Max),底层用类似LongAdder的分段结构,且精度满足监控要求 -
Timer:专为耗时类指标设计,内置滑动窗口聚合,比手动维护多个 Counter 更高效、语义更清晰
它们比“自己造轮子 + LongAdder”更安全、更符合 Micrometer 设计哲学。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











