简体   繁体   English

易失性变量和非易失性重新排序/可见性

[英]Volatile variable and non volatile reordering / visibility

So I thought I knew this stuff well enough, until I read something which got me doubting my knowledge on this subject matter. 因此,我以为我对这方面的知识已经足够了解了,直到我读了一些东西,使我怀疑我对此主题的了解。 I am almost certain the book is incorrect but would like to ask the community as well. 我几乎可以肯定这本书是不正确的,但也想问一下社区。

PS: Have not seen the errata of the book so could well be disclosed as an error. PS:尚未看到该书的勘误表,因此很可能被公开为错误。

A simplified example: 一个简化的例子:

public class VolatileMain {

private volatile int a = 0;
private String text = "";

public static void main(String[] args) throws Exception {

    VolatileMain vm = new VolatileMain();

    Thread writer = new Thread() {

        @Override
        public void run() {
            System.out.println("Running thread " + Thread.currentThread().getName());
            vm.text = "hello world";
            vm.a = 5;
        }
    };

    writer.start();
    writer.join();

    System.out.println("Running thread " + Thread.currentThread().getName());
    System.out.println(vm.a);
    System.out.println(vm.text);

   }

}

So given the example is it correct to assume that the write to "text" by Thread writer is guaranteed to be visible by any other thread that reads it? 因此,以给出的示例为前提,假设可以保证任何其他读取该线程的线程都可以看到Thread writer对“文本”的写入是否正确?

It seems the author is piggy backing on the volatile semantics of the variable "a" and ensuring that the write to "text" will also be flushed when "a" is flushed, is this a guarantee? 看来作者很支持变量“ a”的易变语义,并确保当刷新“ a”时也刷新对“ text”的写操作,这是否可以保证?

I didn't think it was, but my own quick test (above) to the contrary 我不认为是这样,但是我自己的快速测试(上面)与此相反

Your thoughts. 你的意见。

is it correct to assume that the write to "text" by Thread writer is guaranteed to be visible by any other thread that reads it? 假设由线程编写器对“文本”的写入保证可以被任何其他读取它的线程可见,是否正确?

No. But it's guaranteed to be visible by any other thread that reads a before reading text , as your example does: 不会。但是可以保证任何其他读取a线程在读取text之前都可见,如您的示例所示:

  • the write of text happens-before the write to a in the writer thread 写入text发生在写入线程中写入a之前
  • the write of a in writer happens-before the read of a in the main thread 的写a在作家的之前发生的读取a在主线程
  • the happens-before relation is transitive 关系发生前的传递
  • hence the the write of text happens before the read of a . 因此的写text的读取之前发生a

No, it's not guaranteed, because the "flushing" is not that simple. 不,不能保证,因为“冲洗”不是那么简单。 Even if you actually write non-volatile something into the "main memory", it does not guarantee that consequent reads in other threads will read it from that main memory. 即使您实际上将非易失性内容写入“主内存”,也不能保证其他线程的后续读取操作也会从该主内存中读取它。 Consider the following example: 考虑以下示例:

public class VolatileMain {

    private volatile int a = 0;
    private String text = "";

    public static void main(String[] args) throws Exception {

        VolatileMain vm = new VolatileMain();

        Thread writer = new Thread() {

            @Override
            public void run() {
                // Added sleep here, so waitForText method has chance to JIT-compile
                LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));
                System.out.println("Running thread " + Thread.currentThread().getName());
                vm.text = "hello world";
                vm.a = 5;
                System.out.println("Text changed!");
            }
        };

        writer.start();

        waitForText(vm);

        writer.join();

        System.out.println("Running thread " + Thread.currentThread().getName());
        System.out.println(vm.a);
        System.out.println(vm.text);

    }

    // Wait for text change in the spin-loop
    private static void waitForText(VolatileMain vm) {
        int i = 0;
    /*
        @Edit by Soner
        Compiler may do following steps to optimize in lieu.
        String myCache = vm.text;
        -- Assume that here myCache is "" -- so stay forever.
        while (myCache.equals("")) { i++; }
    */
        while (vm.text.equals("")) {
            i++;
        }
        System.out.println("Wait complete: " + i);
    }
}

There are quite good chances that waitForText will never finish, just because JIT-compiler will optimize it and move the reading of the vm.text out of the loop (as it's not volatile, no volatile reads are performed in the loop and text never changes inside the loop) making the loop infinite. 仅因为JIT编译器会优化它并将vm.text的读取移出循环(因为它不是易失性的,所以在循环中不执行易失性读取并且text永远不会更改), waitForText可能永远不会完成在循环内)使循环无限。

Volatile read/write not only affects the memory commitment, but also changes the JIT-compilation strategy. 易失的读/写操作不仅会影响内存分配,还会更改JIT编译策略。 Add reading of vm.a in the while-loop and the program will work correctly. 在while循环中添加vm.a读取,程序将正常运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM