【问题标题】:visibility difference between synchronization of field reads and volatile字段读取同步和易失性之间的可见性差异
【发布时间】:2014-04-28 11:28:54
【问题描述】:

我已阅读 SO 的以下文章

Difference between synchronization of field reads and volatile

这里提问者写道

同步的重点是保证 此线程读取的 acct.balance 是最新的,并且任何 对 acct.balance 中对象字段的未决写入也是 写入主存。

最受欢迎的答案:

你是对的。

请研究此代码:

public class VolatileTest {

    static/* volatile */boolean done = false;

    public VolatileTest() {
        synchronized (this) {

        }

    }

    public static void main(String[] args) throws Exception {
        Runnable waiter = new Runnable() {
            public void run() {
                while (!done)
                    ;
                System.out.println("Exited loop");

            }
        };
        new Thread(waiter).start();
        Thread.sleep(100); // wait for JIT compilation
        synchronized (VolatileTest.class) {
            done = true;
        }
        System.out.println("done is true ");
    }

}

在我的电脑上,这个程序不会终止。

所以我认为

  1. 如果我更改 volatile 变量,我将在另一个线程中看到实际值 适合任何地方的杰出人士!
  2. 如果我用监视器“A”更改同步部分的变量,我会 仅在具有监视器“A”的同步部分中查看实际值(例如在另一个线程中)

我说的对吗?

【问题讨论】:

  • 术语“主存储器”在这里是一个松散的术语。它只意味着一致的观点。 L2 缓存相互通信以实现这一点,因此更改甚至可能不在 L3 缓存中,更不用说主内存了。
  • @Peter Lawrey 我对 CPU 的了解不够。我什么都不懂
  • 这将讨论缓存如何保持同步。 en.wikipedia.org/wiki/Cache_coherence 通常缓存相互通信,绕过主内存,因为缓存更快,设计。这将告诉你更多关于 CPU 缓存的知识en.wikipedia.org/wiki/CPU_cache

标签: java multithreading synchronization volatile thread-synchronization


【解决方案1】:
  1. 是的,这是真的,因为 volatile writehappens-before 写入的值可以从变量中读取。
  2. 不完全是。可以保证同一监视器上的另一个线程 synchronized 将看到更新的值,因为监视器释放 happens-before 同一监视器被另一个线程获取。在不获取相同监视器的情况下,其他线程可能看到更新的值。你的表述中的“唯一”太强了:)

【讨论】:

  • 但是即使没有监视器,线程最终应该会看到更新的值吗?即使在这种情况下内存在一段时间内不一致,循环也不应该在一段时间后检测到翻转的布尔值吗?
  • @monocell 这可能会发生,也不会发生 - 我们无法保证何时会发生,以及是否会发生。
  • 我不知道,尽管任何转换都可以通过重新排序语句的效果来解释,而不是完全没有效果。并认为人们为我编写 Java 代码付钱。 :(
  • @Alexey 虽然我同意这个答案 - Core i7 会进行无序和一般的重新排序(并且总是这样做)。我会很惊讶他们在高性能芯片中弃用了如此重要的加速技术。
  • @monocell 多线程导致的不终止的情况在这里被简单地触及。事实上,整个事情都是非常数学化的,很难理解它。这种特殊情况是由于“此外,如果动作 y 在 O 中,并且 hb(x, y) 或 so(x, y),那么 x 在 O 中”这表示 - 除非存在死锁(或其他类型的锁),如果存在 HB 关系 - 你最终会看到变量的变化。但是,这并没有说明没有 HB 会发生什么。事实上,它甚至将挂起定义为允许的,以防线程中没有任何动作可以阻止它。
【解决方案2】:

你是对的)

这里描述了内存模型:Java Memory Model

特别指出:

监视器上的解锁发生在该监视器上的每个后续锁定之前 监视器。

与

对 volatile 字段(第 8.3.1.4 节)的写入发生在每个后续 读取该字段。

因此,只有在同一监视器上的锁定和解锁才会按照您的意愿行事,对 volatile 变量的所有写入和读取也是如此。因此,您的程序可能不会终止,因为您在没有锁定所述监视器的情况下阅读并且没有发生之前的关系。

需要注意的一点(这就是多线程错误如此烦人的原因):
您可能会看到其他线程的变化。或者可能不会。在大多数架构上,您可能会在正常处理期间看到它,并且可能会在高负载期间出现错误,从而难以重现。如果没有发生之前(即易失性、同步、在同一线程中或链接中的其他情况),JVM 不保证什么和什么时候会看到它,但尽量平稳运行。

【讨论】:

    猜你喜欢
    • 2011-03-07
    • 2018-01-18
    • 1970-01-01
    • 2016-05-31
    • 2012-07-30
    • 2022-08-16
    • 2023-03-21
    • 2019-09-28
    • 2020-08-13
    相关资源
    最近更新 更多