簡體   English   中英

Java volatile變量和同步獲取器

[英]Java volatile variables and synchonized getters

我正在用Java並發與Java“實踐中的並發”一書進行一些實驗。

我還有一個問題:同步的getter方法是否等於volatile私有變量? 一些代碼:

public class ConsoleApp {

private static boolean ready;
private static int number;



public synchronized static boolean isReady() {
    return ready;
}



private static class ReaderThread extends Thread {
    public void run() {
        while (!isReady()) {
            //Thread.yield();  // jvm updates variables on sleeping thread sometimes 
        }
        System.out.println("from class: " + number);
    }
}



public static void main ( String [] arguments ) throws InterruptedException
{
    System.out.println("start");
    Thread.sleep(3000);

    ready = false;
    number = 23;

    System.out.println("inited variable");
    Thread.sleep(3000);

    new ReaderThread().start();

    System.out.println("thread started");
    Thread.sleep(3000);

    number = 42;
    ready = true;

    System.out.println("variables changed");
    Thread.sleep(3000);


    System.out.println("ended;: " + number);
    Thread.sleep(8000);
    System.out.println("end" + number);

}
}

這個變體給我“ from class:”消息。

private static volatile boolean ready;
...
while (!ready) {
    ...
}

在代碼執行方面有什么區別? 我知道“ synchonized”表示“一個線程訪問”,而volatile表示“從主線程獲取值而不進行緩存”

但是為什么兩個變體的行為是相同的?

同步getter方法是否等於volatile私有變量?

沒有。

等價於synchronized setter和synchronized getter與volatile private變量。

如果使用synchronized getter和普通的(非同步的)setter,則一個線程的寫操作與另一個線程的(后續)變量讀操作之間將不存在先於關系。

另一點是,getter和setter需要在同一件事上進行同步。

最后,getter或setter可能比讀或寫變量要復雜得多。 在那種情況下, synchronized構造可確保相對於在同一鎖上同步的其他線程原子地執行所有操作。 相比之下, volatile變量不能給您原子性保證。


但是,為什么兩個變體的行為相同?

你真是幸運!

Java內存模型說, 保證讀取操作可以看到先前讀取操作的結果。 如果以正確的方式進行操作,則應用程序將正確運行。

相反,情況並非如此。 如果你這樣做不對,你可能會得到正確的行為,但無論如何也不能保證(!):

  • 您可能總是在特定平台上獲得它。 例如具有給定數量的內核和給定的內存架構的硬件。

  • 您可以始終使用給定版本的Java來獲得它。

  • 否則,您可能會得到正確的行為……除非在罕見或異常情況下。

但是,如果您不執行Java內存模型所需的操作,則無法保證行為。


有幾個后果:

  • 測試不會證明您正確的並發代碼是正確的。 充其量它可能表明它是不正確的。

  • 測試以證明不正確的並發代碼不正確也很困難。 您可能會發現您的錯誤代碼似乎可以正常工作。

只有釋放並隨后獲取鎖才能建立事前關系。 因此,您需要使用synchronized方法來獲取和設置變量ready值。

但是在您的示例中, ready volatile就足夠了,因為可以為變量設置新值,並從變量獲取值也要建立在before-before之前

最主要的是,您需要保持一致。 如果對變量使用synchronized方法,則僅應通過那些synchronized方法對變量進行所有訪問。

您的問題中的示例將始終可預測地使用volatile但只有將setter標記為“ synchronized 時,才會發生

ready = true;

在主線程中

while (!isReady()) 

在第二個線程。 因此,JVM內存模型不能保證此處沒有任何內容。 它可能會按您期望的方式工作,而不是您所期望的方式工作。

並發的問題是您需要降低對確定性和可預測性的期望。 當您執行單線程操作時,計算機就是這樣做的。 現在您正在處理並發,它將不再需要。 您不能問“為什么這樣或那樣”或“為什么這樣或那樣”? 您所要問的是是否有保證。

當您使用自己的工具版本在計算機上嘗試使用這兩種代碼時,它們會產生相同的結果,因為沒有強制要求它們會表現出不同的行為。 沒有任何規定要求他們會做同樣的事情。 這些代碼會發生某些事情,您可以預測其中的一些,而不能預測其余的。

無論如何,與同步方法或volatile變量的區別在於,可以保證對volatile變量的任何訪問,讀取或寫入,就好像只有一個主內存而線程本地內存緩存不存在一樣。 同步方法僅在您輸入同步方法時才這樣做。 在這里,您僅在讀取變量時進行同步,並且在寫入時未進行任何適當的處理。

這意味着您的帶有同步的示例有很多機會無法給出預期的結果。 並不是說它一定會失敗。 也許會,也許不會。 我們所能說的就是正確編程所能保證的。 我們無法確定您的編程未知后會發生什么。

暫無
暫無

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

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