【问题标题】:volatile + synchronized combination in multithreaded scenario多线程场景中的 volatile + synchronized 组合
【发布时间】:2016-06-27 14:29:18
【问题描述】:

我对@9​​87654322@内部有很好的了解,对volatile内部有基本的了解。

我有一个查询,这是对以下 SE 问题的跟进:

Why is volatile used in this example of double checked locking

我对某一部分感到困惑。如果我严格遵循上面的示例,我必须将volatile 添加到许多变量中,但我确信所有多线程应用程序都没有使用这种 volatile + synchronized 组合。

如果synchronization 不保证内存一致性,如上例所示,使用volatile 变量与synchronization 代码组合的用例是什么?

【问题讨论】:

  • 注意:实际上对单例使用双重检查锁定是一个坏主意,因为a)单例,b)您可以使用enum
  • 我肯定会使用其他一些选项,例如枚举、延迟加载程序。刚刚添加了该问题以通过一些工作代码提供上下文。我的困惑只是关于组合。
  • 双重检查锁定用于单例对象的延迟初始化。此外,volatile 可以防止可能发生的优化:“此关键字可以防止编译器尝试优化代码以便在对象完成构建之前对其进行访问的微妙情况”(OCP Oracle® Certified Professional Java® SE 8 Programmer二)
  • 如果您不在 Singleton 上使用其他静态方法或变量,则 Singleton 最简单的解决方案是使用 Eager 解决方案。另一种方法是使用枚举,或使用持有者类成语

标签: java multithreading synchronized volatile


【解决方案1】:

当一个变量被定义volatile 时,它是从主内存而不是注册表中读取的。

所以每个处理器都会看到相同的值。

在仔细检查中,变量被定义为volatile,以确保在synchronized块之外进行检查将拦截大多数情况。

如果变量不是volatile,则代码将起作用,但如果您有更多处理器,则可以在synchronized 块内进行超出需要的操作(变量不为空时也是如此)。

如果对变量的所有访问都在 synchronized 块中完成,则根本不需要 volatile

【讨论】:

  • 你最后的陈述是否也适用于上述双重锁定代码?
  • 双重检查?还是双锁?
  • 可能是 OP 的拼写错误。应该是仔细检查。
  • 谢谢@DavideLorenzoMARINO。您的最后一条语句(如果对变量的所有访问都在同步块中完成,则根本不需要 volatile。)消除了我的困惑:)
  • >If all access to a variable are done in the synchronized block a volatile is not necessary at all. 这是如何执行的? Volatile 是关于可见性, synchronized 是关于使操作原子化。在我看来,这是两件不同的事情。
【解决方案2】:

该问题/答案中volatilesynchronized 的组合仅适用于双重检查锁定

如果您没有进行双重检查锁定,并且始终在同一 synchronized 监视器的保护下访问您的共享变量(如果应用程序不使用 @ 987654325@ classes),那么你就不需要volatile

无论如何,这并不意味着双重检查锁定是一个好主意。尽管volatile + synchronized 构造可以使双重检查锁定起作用,但它并没有提供任何显着的性能优势,因为您也可以阅读@alf 对您所指问题的回答。

【讨论】:

  • 但在上述场景中,作者出于内存一致性的目的使用了组合。在单一锁定条件下,上面的代码不起作用。
  • @AdityaW 上面的代码是双重检查锁定,而对于双重检查锁定(您访问synchronized 块之外的共享变量),您需要volatile。大多数多线程代码(幸运的是!)没有双重检查锁定,因为它几乎没有好处。
  • @AdityaW gist.github.com/anonymous/52e40d36d7e4caf321630bf71261a7b4 非常好。双重检查代码中的问题源于对共享(部分初始化但已经可见)变量的非同步和非易失性访问(通过第二个线程),内存保证为 0。