簡體   English   中英

Java重新排序和易變問題

[英]Java reordering and volatile issue

我最近遇到了一個有趣的問題:

例如我有A類:

class A {
  int a;
  int b;

  public A() {
    a = 1;
    b = 2;
  }

  public int getA() {return a;}
  public int getB() {return b;}
}

A類必須僅作為單身存在; 所以,為了提供對單例的訪問,我創建了類Factory:

class Factory {
private A a;
public A getA() {
    if (a == null) {
        synchronized(this) {
            if (a == null) {
                a = new A();
            }
        }
    }
    return a;
}

}

據我了解,根據JVM中的重新排序,如果2個線程同時訪問Factory.getA(),則其中一個線程可能會獲得部分構造的對象,並且可能導致應用程序崩潰。

但如果我私有A a; 我可以確定每個線程只能訪問完全構造的對象嗎?

因此,作為結論,如果我將變量x標記為volatile,它是否會影響x.class構造函數的內容?

但如果我私有A a; 我可以確定每個線程只能訪問完全構造的對象嗎?

是。

考慮線程#1初始化a 工廠互斥鎖確保一次只有一個線程可以創建A 所以我們有以下順序:

  1. 線程#1看到a為空

  2. 線程#1獲取互斥鎖。

  3. 線程#1看到a仍為null。

  4. 線程#1構造一個A實例,初始化其變量。

  5. 線程#1將實例引用分配給a

  6. 線程#1釋放鎖定。

一個線程#2看到a在非空狀態(例如,在任一步驟#1或步驟#3的測試),這意味着,線程#1必須已經變得到(至少)步驟#5。 但這意味着

A.a = 1;                    // thread #1

“之前發生”

A.b = 2;                    // thread #1

“發生在之前”

write to Factory.a          // thread #1

“發生在之前”

read from Factory.a         // thread #2

“發生在之前”

access to some field of `A` // thread #2

因為從線程#1中的新A的字段初始化到線程#2中的字段訪問,存在完整的“發生在之前”關系鏈。 這意味着線程#2 保證看到字段初始化寫入的結果......除非對這些字段有其他干預寫入。

請注意,它是寫入volatile a之間的“發生之前”,而后續讀取是另一個作為鍵的線程。 如果a不是易失性的,那么就沒有完整的“先發生過”鏈, 因此執行不是“發生 - 在一致之前”; 即它包含“數據競賽”。

我上面使用/遵循的術語和推理來自Java語言規范, 第17.4章

揮發物

Java volatile關鍵字用於將Java變量標記為“存儲在主存儲器中”。 更確切地說,這意味着,每次讀取一個volatile變量都將從計算機的主內存中讀取,而不是從CPU緩存中讀取,並且每次寫入volatile變量都將寫入主內存,而不僅僅是CPU緩存。

它與記憶有關。 它不會影響構造函數。

來源:點擊這里

既然你想讓A成為一個單身人士,我的建議就是讓A成為一個枚舉。
使用枚舉:

  • 將解決您的問題,因為您沒有部分構造的枚舉
  • 並且使用枚舉是實現單例模式的最佳方式。

     enum A{ INSTANCE(1,2); private int a; private int b; A(int a,int b){ this.a=a; this.b=b; } public int getA(){ return a; } public int getB(){ return b; } } 

規范和實現有兩種不同的東西。 如果你制作a volatile,那將解決實際問題,因為JVM會在賦值后插入一個商店障礙。 但是這仍然會破壞規范,因為volatile不是final ,它不能保證會發布一個對象的所有子對象。 順便說一句,在具有強大內存模型的CPU上,很多不良代碼都可以正常工作,但這並不意味着你應該這樣寫。

暫無
暫無

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

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