【问题标题】:Overhead of multiple synchronized on the same object同一对象上多个同步的开销
【发布时间】:2018-11-15 16:23:06
【问题描述】:

考虑这段代码:

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

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

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

【问题讨论】:

  • “开销”是什么意思?内存消耗?运行?还有什么?
  • 我的意思是运行时!
  • 会更多,因为同步被调用的次数是单次调用用例的 1000 倍。因此,VM 必须在 obj 上添加 1000 个额外的锁定令牌。
  • @Korashen 不明白它已经获得了锁吗?

标签: java synchronized


【解决方案1】:

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

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

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

    synchronized 锁是可重入的,并且当线程已经持有锁时获取锁是 a) 检查它是否已经持有锁的时间,b) 增加计数器然后减少它的时间。

    第一个耗时最长,每次增加约 10 - 50 ns。

    【讨论】:

      猜你喜欢
      • 2010-09-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-03
      • 2014-05-20
      相关资源
      最近更新 更多