字段读取同步和volatile之间的可见性差异

时间:2014-04-28 11:28:54

标签: java multithreading synchronization volatile thread-synchronization

我已阅读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"(例如在另一个线程中)
  3. 我说错了吗?

2 个答案:

答案 0 :(得分:3)

  1. 是的,这是正确的,因为易变写发生 - 在之前写入的值可以从变量中读取。
  2. 不完全是。可以保证同一监视器上的另一个线程synchronized将看到更新的值,因为监视器释放发生在同一监视器由另一个线程获取之前。如果没有获取相同的监视器,其他线程可能会看到更新的值。你的配方中“唯一”太强了:)

答案 1 :(得分:2)

你是对的)

此处描述了内存模型:Java Memory Model

特别指出:

  

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

AND

  

对易失性字段(第8.3.1.4节)的写入发生在每次后续之前   阅读该领域。

因此,只有同一台显示器上的锁定和解锁才能表现出你想要的,也是所有写入和读取volatile变量的行为。因此,您的程序可能无法终止,因为您在没有锁定监视器的情况下阅读并且之前没有发生关系。

有一点需要注意(这就是多线程错误如此烦人的原因):
您可以在其他线程中看到更改。或者可能没有。在大多数体系结构中,您可能会在正常处理过程中看到它,并且在高负载期间可能会出现错误,从而难以重现。如果之前没有发生事件(即易失性,同步,在同一个线程或链接中的其他情况下),JVM不会保证什么时候会看到它,但是尝试它最好顺利运行。