【问题标题】:Does empty synchronized(this){} have any meaning to memory visibility between threads?空的 synchronized(this){} 对线程之间的内存可见性有什么意义吗?
【发布时间】:2014-07-17 09:18:36
【问题描述】:

我在 StackOverflow 上的一条好评评论中读到了这篇文章:

但是如果你想安全的话,你可以添加简单的 synchronized(this) {} 在你的最后@PostConstruct [方法]

[注意变量不是易变的]

我在想 happens-before 只有在写入和读取都在 synchronized 块中执行或至少读取是易失性的情况下才会强制执行。

引用的句子正确吗?空的synchronized(this) {} 块是否会将当前方法中更改的所有变量刷新到“一般可见”内存?

请考虑一些场景

  • 如果第二个线程从不对this 调用锁怎么办? (假设第二个线程以其他方法读取)。请记住,问题是关于:将更改刷新到其他线程,而不是为其他线程提供一种方式(同步)来轮询原始线程所做的更改。在 Spring @PostConstruct 上下文中,其他方法也很可能不同步 - 正如原始评论所说。

  • 是否仅在另一个线程的第二次和后续调用中强制更改的内存可见性? (请记住,这个同步块是我们方法中的最后一次调用)——这会将这种同步方式标记为非常糟糕的做法(第一次调用中的值过时)

【问题讨论】:

    标签: java concurrency synchronized volatile memory-model


    【解决方案1】:

    很遗憾,关于 SO 的大部分内容,包括该线程中的许多答案/cmets,都是错误的。

    此处适用的 Java 内存模型中的关键规则是:在给定监视器上的解锁操作在此之前发生在同一监视器上的后续锁定操作。如果只有一个线程获得了锁,它就没有任何意义。如果虚拟机可以证明锁对象是线程受限的,它可以忽略它可能发出的任何栅栏。

    您强调的引用假设释放锁充当完整的围栏。有时这可能是真的,但你不能指望它。所以你的怀疑问题是有根据的。

    有关 Java 内存模型的更多信息,请参阅 Java Concurrency in Practice,第 16 章。

    【讨论】:

      【解决方案2】:

      monitor exit 之前发生的所有写入对monitor enter 之后的所有线程都是可见的。

      synchronized(this){} 可以像这样转换成字节码

      monitorenter
      monitorexit
      

      因此,如果您在 synchronized(this){} 之前有大量写入,它们会在 monitorexit 之前发生。

      这将我们带到我第一句话的下一点。

      monitor enter 之后对所有线程可见

      所以现在,为了让线程确保发生写入,它必须执行相同的同步,即synchornized(this){}。这将至少发出一个monitorenter 并在订购前确定您的情况。


      所以回答你的问题

      空的 synchronized(this) {} 块是否刷新所有已更改的变量 在当前方法中“一般可见”内存?

      是的,只要您在要读取那些非易失性变量时保持相同的同步即可。

      解决您的其他问题

      如果第二个线程从不对此调用 lock 怎么办? (假设第二个 线程以其他方法读取)。请记住,问题是关于:冲洗 改变其他线程,不给其他线程一个办法(同步) 轮询原始线程所做的更改。也没有同步 其他方法很可能在 Spring @PostConstruct 上下文中

      在这种情况下,在没有任何其他上下文的情况下使用synchronized(this) 是相对没用的。没有发生之前的关系,理论上它和不包括它一样有用。

      是仅在第二个和后续强制更改的内存可见性 由另一个线程调用? (请记住,这个同步块是 我们方法中的最后一次调用) - 这将标记这种同步方式 作为非常糟糕的做法(第一次调用中的陈旧值)

      内存可见性是由调用synchronized(this) 的第一个线程强制执行的,因为它将直接写入内存。现在,这并不一定意味着每个线程都需要直接从内存中读取。他们仍然可以从自己的处理器缓存中读取。线程调用synchronized(this) 确保它从内存中提取字段的值并检索最新的值。

      【讨论】:

      • 但是,如果我说:当第二个线程在更改后第一次调用此方法时,它可以获得一个陈旧的值,因为 100% 确定更新发生在第二个线程退出此方法时(空同步是方法中的最后一个),因此更改将 100% 确定仅在 SECOND 其他线程调用中传播。我说的对吗?
      • 你的推理是正确的。因此,如果 synchornized 像您所说的那样位于末尾,并且另一个线程进入该方法,则任何非易失性变量肯定会过时,直到该线程到达 synchronized 块。
      • 话虽如此,该推理也适用于读取这些非易失性变量的任何线程,而在读取之前没有synchronized 块(即使不在@PostConstruct 方法中)。
      猜你喜欢
      • 2012-02-21
      • 2017-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-22
      • 1970-01-01
      • 1970-01-01
      • 2011-03-08
      相关资源
      最近更新 更多