2014-01-07 23 views
0

考慮下面的玩具例子:Java,與主內存的同步僅對同步塊或所有實例變量中的變量發生?

public class Test { 

    private int nr; 
    private A a; 


    public synchronized A getA() { 
     return a; 
    } 

    public synchronized void setA(A a) { 
     this.a = a; 
    } 


    public static class A{ 
     B b; 
    } 

    public static class B{ 
     // may contain other references 
    } 
} 

由於同步關鍵字都在的get/set,我們可以安全地讀/寫「一」從多個線程。我們不需要聲明它是volatile,因爲在同步塊的開始/結束處,本地緩存線程與主內存的同步已完成。

但是,基本上我們正在同步'this',Test對象也包含變量'nr'。 Java是否與主內存同步,關於set/get'a',只發生在'a'上,因爲它是在這些同步塊或者Test類的所有成員中訪問的唯一變量,因爲我們已經同步了'this 「?另外,當發生與主存儲器的同步時,是否是遞歸的,也就是說'a'可能包含的所有引用鏈(在本例中爲'b')是從主存儲器讀取還是隻讀?

謝謝

+0

理論上,只有a是同步的,儘管在實踐中nr不可能同步。 – assylias

+0

@assylias我認爲該陳述是不正確的。如果兩個線程在同一個對象上同步,則所有寫入nr的內容都將保證可見。 – Ishtar

+0

Similar questions:http://stackoverflow.com/questions/6611109/how-will-the-following-java-program-run-in-the-happens-before-order-example'X'對應'nr'和'Y'到'this.a' – Ishtar

回答

0

Javadoc中的一個線程寫的concurrent package

結果都保證到通過僅在寫操作之前發生的讀操作另一個線程讀取可見。

...

一種監視器的開鎖(同步塊或方法出口)之前發生同一監視器的每個後續鎖(同步塊或方法條目)。並且因爲發生在before關係是傳遞性的,所以在解鎖之前的線程的所有動作發生在任何線程鎖定該監視器之後的所有動作之前。

所以你可以說所有的變量都是同步的。更準確一點說,所有的寫入(對任何變量)都可以通過讀取(來自相同變量)如果兩個線程​​在同一個對象上看到。

如果您有多個線程A,B和C.例如,線程A和B在test1上同步,線程B和C在test2上同步。然後B將看到A的寫入。線程C將看到B的寫入,並且可能看到或可能看不到A的寫入。

實施例與兩個線程T1和T2,語句被執行從上到下

T1    T2 
test.nr = 5; 
test.setA(a1); 
       test.getA(); //gives a1 
       print(test.nr); //gives 5 

隨着不同的對象:

T1    T2 
test2.nr = 5; 
test.setA(a1); 
       test.getA(); //gives a1 
       print(test2.nr); //gives 5 

上一頁實施例不正確同步:

T1    T2 
       test.nr = 6; 
test.nr = 5; 
test.setA(a1); 
       test.getA(); //gives a1 
       print(test.nr); //gives 5 or 6 

再舉一個例子

T1    T2    T3 
test.nr = 5; 
test.setA(a1); 
       test.nr = 6; 
       test.setA(a2); 
           test.nr = 7; 
           test.getA(); //gives a2 
           print(test.nr); //gives 5,6 or 7 

什麼是「主內存」? 5,6或7?恩,好吧,在說明書中有沒有主存儲器。所以沒有正確的答案。 JVM當然會使用主內存,寄存器和緩存。但是它如何使用它們取決於JVM的實現。

最後一個可怕的例子

T1    T2 

       test.nr = 6; 
       print(test.nr); //gives 5 or 6 
test.nr = 5; 

可以讀取值5,即使其沒有寫!

有一個簡單的解決方案,如果兩個線程讀取和/或寫入同一個變量,將它們放入同一個對象的同步塊中。

+0

您好,我有點困惑。我的意思是我知道使用同步(順序訪問可能從不同線程運行的代碼),但是與主內存的同步使我變得「霧」。假設我們輸入getA。我假設'a'將從主內存中讀取。但是既然我們在'this'上同步,'nr'也會從內存中讀取(因爲它是一個實例變量,屬於我們用來同步的'this')? – adragomir

+0

@ user3030447 JLS中沒有主內存。所以它不可能談論從主內存中讀取。如果在寫入之前發生寫入(並且在之前沒有發生其他寫入),則線程將看到寫入。 – Ishtar

-1

所有​​確實是獲得this對象上的鎖。如果一個線程調用一個同步方法,則另一個線程嘗試在同一個對象上調用一個同步方法,第二個調用將阻塞,直到第一個調用完成並釋放該鎖。​​不會對訪問對象的字段有任何限制。

0

無論您的方法中訪問的字段是什麼都不相關......只能同時執行一個同步方法。如果要鎖定不同的字段,則需要手動同步。

對於第二個問題,這種同步方法是「可重入的」,這意味着您可以遞歸調用同一個方法或任何其他同步方法而不用擔心死鎖。

+0

我指的是http://javamex.com/tutorials/synchronization_concurrency_synchronized2.shtml,另外:「如果我們讀取和寫入來自不同線程的同步塊內的變量,我們總是希望線程1看到由線程設置的值2;只是看到寄存器中的本地緩存副本是不正確的,因此在進入和退出同步塊時,必須對主存儲器進行相關的讀/寫操作,並且必須以正確的順序進行「。 – adragomir

+0

鏈接中的規範是在編寫自己的Java虛擬機時幫助指導一個人:確保同步方法按照規範行事。你絕對不是在編寫JVM(你問的問題是Java,不是彙編,C/C++或其他)。該規範適用於「JVM作者視角」。對於「Java程序員」透視圖(您),請參閱http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.3.6 –