jmh量化synchronized开销的关键是明确场景、对比对象和干扰排除:单线程无竞争测基础成本(如monitor entry/exit),多线程低竞争测锁争用,高竞争测吞吐下降;必须区分这三种情况。

直接用 JMH 量化 synchronized 的性能开销,关键不是“测它慢不慢”,而是明确测什么场景、和谁比、怎么排除干扰。JMH 能给出纳秒级可信数据,但前提是设计合理。
明确对比维度:单线程 vs 多线程竞争
同步开销只在有竞争时显现。JMH 必须区分三种典型情况:
- 单线程无竞争:验证基础成本(如 monitor entry/exit 的固定开销),通常极低(
- 低竞争(如 4–8 线程):模拟常见服务端并发,反映锁轻度争用下的延迟增长
- 高竞争(如 16–32 线程):暴露锁膨胀、线程挂起/唤醒、自旋失败等真实瓶颈
构造可比基准:必须带对照组
单独跑一个 @Benchmark 方法测 synchronized 没意义。必须并列测试:
- 空方法调用(baseline):纯方法调用开销,用于扣除函数调用本身成本
- volatile 写+读:仅保证可见性,无互斥,作为轻量级同步参照
-
AtomicInteger.incrementAndGet():CAS 实现的原子操作,常作为
synchronized的现代替代 - synchronized 块或方法:锁定同一对象,执行相同逻辑(如简单计数器递增)
所有方法操作的数据结构、字段访问方式、循环体内容需严格一致,否则结果不可比。
关键配置要点:避开 JVM 干扰
JMH 不是“写个注解就完事”,以下配置直接影响结果可信度:
- @Fork(3):至少分叉 3 个独立 JVM 进程,避免 GC、JIT 残留状态污染
- @Warmup(iterations = 5, time = 2, timeUnit = TimeUnit.SECONDS):充分预热,让 JIT 完成锁优化(如偏向锁撤销、轻量级锁升级)
- @Measurement(iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS):多轮采样取平均,降低 CPU 频率波动影响
-
@State(Scope.Benchmark):共享被测对象(如计数器实例),确保竞争真实发生;避免
@State(Scope.Thread)导致“伪无竞争” -
禁用逃逸分析干扰:JVM 参数加
-XX:-DoEscapeAnalysis,防止对象被栈上分配而绕过锁逻辑
解读结果时盯住两个指标
运行后看输出中的 Score 列,重点关注:
-
Throughput (ops/s):单位时间完成的操作数。数值越低,说明同步拖得越重。高竞争下
synchronized可能比 Atomic 低 3–10 倍 -
AverageTime (ns/op):单次操作平均耗时。注意看
stdev(标准差)——若标准差极大,说明延迟抖动严重,存在锁争用或 GC 干扰
例如 JDK 17 下,20 线程高竞争场景中:
- AtomicInteger.incrementAndGet():≈ 8.2 ns/op
- synchronized(this) { count++; }:≈ 45–120 ns/op(取决于锁状态和是否触发膨胀)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











