从同步方法调用同步方法的同步开销是多少?

2022-09-02 12:46:33

这两者之间的性能有什么区别吗?

synchronized void x() {
    y();
}

synchronized void y() {
}

和这个

synchronized void x() {
    y();
}

void y() {
}

答案 1

是的,除非 JVM 将调用内联到 ,否则会提供额外的性能开销,而现代 JIT 编译器将在相当短的时间内完成此操作。首先,考虑您介绍的在类外可见的情况。在这种情况下,JVM必须在进入时进行检查,以确保它可以进入对象上的监视器;当调用来自 时,此检查将始终成功,但不能跳过,因为调用可能来自类外部的客户端。此额外检查会产生少量费用。y()y()y()x()

此外,请考虑以下情况:。在这种情况下,编译器仍然没有优化同步;请参阅以下对空的拆解:y()privatey()

private synchronized void y();
  flags: ACC_PRIVATE, ACC_SYNCHRONIZED
  Code:
    stack=0, locals=1, args_size=1
       0: return

根据规范对同步的定义,每个进入块或方法的入口对对象执行锁定操作,离开执行解锁操作。在锁定计数器降至零之前,任何其他线程都无法获取该对象的监视器。据推测,某种静态分析可以证明一个方法只能从其他方法中调用,但是Java的多源文件支持充其量会使它变得脆弱,甚至忽略反射。这意味着 JVM 在输入 y()时仍必须递增计数器:synchronizedprivate synchronizedsynchronized

在调用方法时监视条目,并在返回时监视退出,由 Java 虚拟机的方法调用和返回指令隐式处理,就像使用了 monitorentermonitorexit 一样。synchronized

@AmolSonawane正确地注意到,JVM可以通过执行锁粗化(本质上是内联方法)在运行时优化此代码。在这种情况下,在 JVM 决定执行 JIT 优化后,x()y() 的调用不会产生任何额外的性能开销,但当然,从任何其他位置直接调用仍需要单独获取监视器。y()y()


答案 2

使用 jmh 运行微基准测试的结果

Benchmark                      Mean     Mean error    Units
c.a.p.SO18996783.syncOnce      21.003        0.091  nsec/op
c.a.p.SO18996783.syncTwice     20.937        0.108  nsec/op

=>无统计学差异。

查看生成的组件可发现,尽管锁定已同步,但已执行并内联。y_syncx_sync

完整结果:

Benchmarks: 
# Running: com.assylias.performance.SO18996783.syncOnce
Iteration   1 (5000ms in 1 thread): 21.049 nsec/op
Iteration   2 (5000ms in 1 thread): 21.052 nsec/op
Iteration   3 (5000ms in 1 thread): 20.959 nsec/op
Iteration   4 (5000ms in 1 thread): 20.977 nsec/op
Iteration   5 (5000ms in 1 thread): 20.977 nsec/op

Run result "syncOnce": 21.003 ±(95%) 0.055 ±(99%) 0.091 nsec/op
Run statistics "syncOnce": min = 20.959, avg = 21.003, max = 21.052, stdev = 0.044
Run confidence intervals "syncOnce": 95% [20.948, 21.058], 99% [20.912, 21.094]

Benchmarks: 
com.assylias.performance.SO18996783.syncTwice
Iteration   1 (5000ms in 1 thread): 21.006 nsec/op
Iteration   2 (5000ms in 1 thread): 20.954 nsec/op
Iteration   3 (5000ms in 1 thread): 20.953 nsec/op
Iteration   4 (5000ms in 1 thread): 20.869 nsec/op
Iteration   5 (5000ms in 1 thread): 20.903 nsec/op

Run result "syncTwice": 20.937 ±(95%) 0.065 ±(99%) 0.108 nsec/op
Run statistics "syncTwice": min = 20.869, avg = 20.937, max = 21.006, stdev = 0.052
Run confidence intervals "syncTwice": 95% [20.872, 21.002], 99% [20.829, 21.045]

推荐