【问题标题】:Java - is volatile required with synchronized?Java - 同步需要 volatile 吗?
【发布时间】:2014-05-16 17:36:00
【问题描述】:

在以下简单场景中:

class A {
  int x;
  Object lock;

  ...

  public void method(){
    synchronized(lock){
      // modify/read x and act upon its value
    }
  }
}

x 需要是 volatile 的吗?我知道同步保证原子性,但我不确定可见性......是否锁定 -> 修改 -> 解锁 -> 锁定保证,在第二次锁定之后 x 的值将是“新鲜的”?

【问题讨论】:

  • x 不必是易变的,但lock 必须是最终的。
  • 当然,你必须在持有同一个锁的同时阅读x

标签: java multithreading locking volatile memory-model


【解决方案1】:

不,它没有,因为 synchronized 后面已经插入了内存屏障,所以所有线程都会看到当前线程执行的更新,同时考虑到其他线程将在同一个锁。

Volatile 与同步一样,具有附加的内存屏障 - 根据 CPU,它是存储/加载/完整屏障,可确保来自一个线程的更新对其他线程可见。 我假设这是在 CPU 缓存失效的情况下执行的

编辑 根据我刚刚阅读的内容,存储缓冲区被刷新到 CPU 缓存中,这就是实现可见性的方式。

【讨论】:

  • JLS 中的什么地方描述了这种行为?
  • @jarnbjo Java 内存模型部分。
  • @jarnbjo 这与 Java 内存模型有关,可见性 是通过内存屏障实现的。
  • 啊,太好了,谢谢。 JLS 内存模型部分似乎简短而简洁,但我永远找不到真正帮助我处理现实情况的相关部分。如果这不是太多要求,您能否指出/粘贴描述此行为的相关位?
  • @Bober02 如果你从事这个行业,迟早你会“见到”Martin Thompson 的优秀博客,我很高兴向你介绍它。 :) mechanical-sympathy.blogspot.com/2011/07/…阅读愉快!
【解决方案2】:

简单的回答:如果线程 A 更新了一个字段,然后释放了一个锁,那么线程 B 将保证在线程 B 获得相同的锁后看到更新。

注意,“释放锁”意味着退出 synchronized 块,“获取相同的锁”意味着在同一个对象上同步。

【讨论】:

  • 在线程 B 获得相同的锁之后这是不正确的。它不必获取锁来查看更新。只需简单的重新阅读就足够了
  • @Eugene 阅读 Java 语言规范中的“内存模型”部分,注意“发生在之前”的关系。如果线程 A 写入一个字段然后解锁一个互斥锁,则写入“发生在”解锁之前。但是如果线程 B 随后读取同一个字段而没有在同一个锁上同步,则不存在“发生在之前”的关系:JVM 让线程 B 看到本地缓存的字段的旧值是完全合法的,无论如何自线程 A 编写以来,已经过了很多实时时间。
  • 不要被您的 JVM 在任何特定日期实际执行的操作与正确实现的 JVM允许执行的操作之间的差异所混淆。
  • 我不得不承认,我一直认为 synchronized 之后插入了一个内存屏障,并且内存将被同步。我仍然没有找到任何参考,但肯定必须在同一个锁上同步才能看到。谢谢!!
  • @Eugene 我可能误解了你在说什么。我在想象一个非易失性的变量,线程 A 将变量写入同步块内,但线程 B 读取变量而不同步。在这种情况下,当线程 A 离开同步块时,新值应该被刷新到内存中;但是在某些架构上,如果线程 B 在不同的核心上运行,它仍然可以在其本地缓存中看到旧值。我不知道这在实践中是否会发生,但 Java 语言规范允许它发生。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多