【问题标题】:Why are synchronize expensive in Java?为什么在 Java 中同步很昂贵?
【发布时间】:2010-12-12 20:49:22
【问题描述】:

我真的是 Java 新手,我读到 synchronized 在 Java 中“非常昂贵”。我只想知道什么东西贵,怎么贵?

谢谢。

【问题讨论】:

  • 天哪!这将打开一罐蠕虫。我什至不想开始......
  • 我认为如果线程需要暂停会很昂贵,就像大多数同步 i/o 调用发生的情况一样

标签: java synchronized


【解决方案1】:

Maybe it's not as bad as you think

它曾经很糟糕(这可能是您读到它“非常昂贵”的原因)。这些模因可能需要很长时间才能消失

同步有多昂贵?

由于涉及缓存刷新和失效的规则,Java 语言中的同步块通常比许多平台提供的临界区设施更昂贵,这些设施通常使用原子的“测试和设置位”机器指令实现。即使程序仅包含在单个处理器上运行的单个线程,同步方法调用仍然比非同步方法调用慢。如果同步确实需要争用锁,性能损失要大得多,因为需要多次线程切换和系统调用。

幸运的是,JVM 的持续改进既提高了 Java 程序的整体性能,又降低了与每个版本同步的相对成本,并且可以预期未来的改进。此外,同步的性能成本经常被夸大。一个著名的消息来源引用了同步方法调用比非同步方法调用慢 50 倍之多。虽然这种说法可能是正确的,但它也具有很大的误导性,导致许多开发人员即使在需要同步的情况下也避免同步。

话虽如此 - 并发编程仍然可能很慢,但现在不是纯粹是 Java 的错。精细锁定和粗糙锁定之间存在权衡。太粗显然不好,但也有可能太细,因为锁的成本不为零。

考虑争用的特定资源很重要。机械硬盘就是一个例子,更多的线程会导致性能更差。

【讨论】:

  • 现在有更新的文章吗?看起来 Java 1.4 在撰写本文时尚未发布。由于这篇文章在谷歌同步性能方面排名前三,人们可能会在阅读这篇旧文章时产生错误的想法。谢谢。
  • @Teddy,这个问题和答案实际上是关于在旧版本的 java 中同步特别慢的地方。现在并发性能的问题与所有其他语言一样。线程根本不是某些人希望它们成为的灵丹妙药。
【解决方案2】:

这很昂贵,因为如果您使用线程,并且多个线程必须经过一段同步的代码,一次只能执行其中一个。

这就像一个瓶颈。

当你使用单线程时它甚至更昂贵,因为它必须检查他是否被允许运行。

如果您减少同步段的使用,您的线程将不必停止查看它们是否可以运行(当然,它们不必共享数据)

可以在here找到同步工作原理的高级概述

http://img20.imageshack.us/img20/2066/monitor28synchronizatioc.png

Java 风格的监视器

【讨论】:

  • 是的,锁越有争议,每个竞争者的等待/阻塞就越多,但我也怀疑挂起每个必须等待的线程比什么都贵。
【解决方案3】:

这并不是 Java 特有的。如果没有正确完成,同步在任何多线程环境中都可能被认为是“昂贵的”。在Java中是否特别糟糕,我不知道。

如果线程使用相同的资源,它会阻止线程并发运行。但是,由于他们确实使用相同的资源,因此没有更好的选择(必须这样做)。

问题在于人们经常保护范围太大的资源。例如,一个设计不佳的程序可能会同步整个对象数组,而不是数组中的每个单独元素(甚至是数组的一部分)。

这意味着试图读取元素 7 的线程必须等待线程读取或写入元素 22。没有必要。如果同步的粒度是元素级别而不是数组级别,那么这两个线程就不会相互干扰。

只有当两个线程试图访问 same 元素时,才会出现资源争用。这就是为什么一般规则是只保护尽可能小的资源(当然要受到同步次数的限制)。

但是,老实说,如果替代方案是由于两个线程争夺单个资源而导致的数据损坏,那么成本有多高并不重要。正确编写您的应用程序,并且只在出现性能问题时担心它们(“首先让它工作然后让它快速工作”是我最喜欢的口头禅)。

【讨论】:

    【解决方案4】:

    IBM 的 article 实际上很好地总结了同步背后的要点。

    由于涉及缓存刷新和失效的规则,Java 语言中的同步块通常比许多平台提供的临界区设施更昂贵,这些设施通常使用原子的“测试和设置位”机器指令实现。即使程序仅包含在单个处理器上运行的单个线程,同步方法调用仍然比非同步方法调用慢。如果同步确实需要争用锁,性能损失要大得多,因为需要多次线程切换和系统调用。

    【讨论】:

    • 请注意,文章引用的最新 Java 版本是 Java 1.3。该版本于 2000 年 5 月发布,并且早已停产。虽然基本事实仍然有些相似(Java 内存模型改变了其中一些),但已经有大量优化使得同步在未经测试的情况下非常便宜。
    • s/unontested/uncontested/ 显然。
    • 当然,在编写多线程代码时,代码块的互斥和内存的一致性都是必不可少的,Java 中没有“聪明”的技巧(只有巧妙地破坏了)避免他们。如果你的两个线程必须交互,你必须同步——纯粹而简单。粒度同步是唯一剩下的问题。
    【解决方案5】:

    其他答案提供了很好的技术细节,我不会尝试复制。

    我会建议您检查文章的日期(以及作者的隐含能力和意识)。在早期的 JVM 中,Java 中的同步非常慢。但是,它最近有了很大的改进,以至于非竞争同步比您想象的要快得多,并且非竞争同步也得到了改进。

    请注意,这个问题可能无关紧要 - 如果您需要同步以确保正确性,则您需要同步以确保正确性。我唯一能看到速度问题的情况是,如果您正在考虑创建一个无锁实现(使用非常高效但复杂的 java.util.concurrent.locks.AbstractQueuedSynchronizer),或者可能正在考虑使用另一种语言来完成您的任务。

    总的来说,我认为最好的结论是同步通常足够快,可以在第一次迭代中使用。与所有性能问题一样,首先为了清晰和正确而编写代码,然后只优化您衡量的内容,使其成为应用程序中昂贵的部分。通常,这不会是同步成本*。

    【讨论】:

      猜你喜欢
      • 2011-06-18
      • 2015-07-12
      • 2017-10-12
      • 2019-05-15
      • 2011-08-03
      • 2016-01-31
      • 2014-05-18
      • 2019-04-08
      • 2018-03-27
      相关资源
      最近更新 更多