簡體   English   中英

Java Volatile Read 的真正作用是什么?

[英]what does Java Volatile Read really do?

我對 java volatile read 有一個非常困惑的問題。

我將展示兩個案例來解釋我的問題。

情況1:

class TestVolatile {
    public boolean running = true;
    public volatile boolean volatileField;

    void run() {
        while(running) {
        }
        System.out.println("stopped.");
    }

    public static void main(String[] args) throws InterruptedException {
        TestVolatile t = new TestVolatile ();

        Thread t1 = new Thread(t::run, "t1");
        t1.start();
        TimeUnit.SECONDS.sleep(1);

        t.running = false;
        t1.join();
    }
}

case1 不會停止,這是完全可以理解的,因為運行不是易失的。

接下來我將展示第二種情況。

案例2:

class TestVolatile {
    public boolean running = true;
    public volatile boolean volatileField;

    void run() {
        while(running) {
            // just add a volatile read, the code will stop . 
            if (volatileField) {
                
            }
        }
        System.out.println("stopped.");
    }

    public static void main(String[] args) throws InterruptedException {
        TestVolatile t = new TestVolatile ();

        Thread t1 = new Thread(t::run, "t1");
        t1.start();
        TimeUnit.SECONDS.sleep(1);

        t.running = false;
        t1.join();
    }
}

正如 case2 所示,在 while 循環中添加 volatile 讀取后,代碼將停止。

所以為什么? running 不是 volatile 字段,代碼不寫 volatileField。 是否有一些發生之前的關系可以解釋case2? 非常感謝您的幫助!

從JMM的角度來看,為什么? 從原生代碼的角度來看,為什么?

我已將 ** -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*TestVolatile.run -XX:PrintAssemblyOptions=intel ** 添加到 jvm 選項以查看 TestVolatile.run 的本機代碼。 但我沒有看到任何關於 volatile 讀取的特殊說明。

我的運行時:

java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

macOS catalina 10.15.5
2.6 GHz 6-Core Intel Core i7 

讀取 volatile 字段不僅能確保您看到它的最新版本。 它還建立了正式的“ 發生前”關系:對該字段的任何寫入都發生在隨后的讀取之前。 這發生在之前意味着讀取線程看到的完整程序的狀態至少與寫入線程在寫入時看到的狀態相同。 這就是導致讀取線程看到非易失性寫入的原因。

這些相同的語義也允許您跨線程發布非線程安全對象,只要它們在發布后沒有被修改。 它們與各種線程安全集合創建的語義相同,如java.util.concurrent文檔中所述。

現在,在您的代碼中,您實際上並沒有在將running設置為 false 后寫入volatileField 那么,happens-before 邊緣從何而來? 實際上——無處可去! 您仍然有數據競爭,JVM 不必向您顯示running的最新版本。 但是許多 JVM 實現只是將 volatile 讀取視為“好的,讓我們刷新所有核心緩存以確保該線程可以看到所有內容”,並且您將從該行為中受益。 一個對 JMM 應用更加吝嗇和精確的 JVM 可能會破壞您的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM