首页 > 解决方案 > 同一对象上多个同步的开销

问题描述

考虑这段代码:

void A() {
    synchronized (obj) {
        for (int i = 0; i < 1000; i++) {
            B();
        }
    }
}

void B() {
    synchronized (obj) {
        // Do something
    }
}

调用 A 时“同步”的开销是多少?它会接近只有一个“同步”的开销吗?

标签: javasynchronized

解决方案


这个(合法的)问题的答案取决于操作系统、硬件和特定的虚拟机实现。

撇开函数调用的成本不谈,在一个操作系统/体系结构(考虑现代处理器/操作系统/虚拟机)上几乎没有成本,而在另一个(考虑纯软件处理器仿真)上成本更高。在单个绿色线程 VM 上,它的成本可能接近于零(调用开销除外)。即使在同等功率的 ARM 和英特尔之间,成本也会有所不同。

synchronized() 通常通过使用 OS 同步原语在 VM 内部实现,并使用一些启发式方法来加速常见情况。反过来,操作系统使用硬件指令和启发式方法来执行此任务。通常,后续获取已获取的同步原语在 OS 中非常有效,并且在典型的生产级 VM 上非常有效。

在现代 Windows/Linux VM 和 Intel/AMD 处理器上,通常它不会花费很多 CPU 周期(假设机器处于空闲状态)并且在低纳秒范围内。

请注意,一般来说,这是一个非常复杂的主题。涉及多层软件、硬件(以及在相同硬件资源上运行的其他任务的影响)。在这里即使是一个很小的子课题的严谨研究也可以组成多个博士学位。论文。

但在实践中,我的建议是假设小循环中第二次同步的成本为零,除非遇到特定的瓶颈(这不太可能)。

如果有大量的迭代,它肯定会比单同步增加成本,整体效果取决于你在循环内做什么。通常,每次迭代都有一些工作使得相对开销可以忽略不计。但在某些情况下,它可能会阻止循环优化并增加大量开销(与单个同步相比相当大,而不是作为实际措施)。但是,在大循环的常见实际情况下,应该考虑不同的设计并避免执行外部同步以减少锁争用

例如,为了了解 VM 实现,您可以查看本文的同步部分。它有点过时,但很容易理解。


推荐阅读