![](/img/trans.png)
[英]Understanding atomic-ness, visibility and reordering of synchonized blocks vs volatile variables in Java
[英]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.